本节内容:

1、url反向解析
2、list_display_links
3、构建添加按钮
4、forms组件的choiceField字段
5、modelform的应用
6、modelform的参数设置
7、stark组件基于modelform实现添加功能

一、url反向解析

设置方法:
1、首先在url路由分发,设置别名,用于反向解析
2、通过别名拿到路径,就可以执行
_url = reverse("%s_%s_delete" % self.app_model, args=(obj.pk,))
# 这里通过别名找到对应的url,然后args会将数字传参给该url形成路径,然后执行相应的视图函数

在stark组件中的做法:
1、将反向解析的名字(),设置成配置了的属性,方便调用
2、具体的页面路径通过方法可以调用,直接得到具体的路径
相关代码示例及详解
sites.py文件

class ModelStark():  # 配置类
    def __init__(self,model):
        self.model = model

        # 配置反向解析的元组即元组里面放APP的名称和表名:(label_name,model_name)
        self.model_name = self.model._meta.model_name # 取到当前模型类的名字(即表名)
        self.app_label = self.model._meta.app_label  # 取到该模型类所在APP名字
        self.app_model = (self.app_label, self.model_name)

    # 反向解析url,执行这里的方法可以动态的获取url路径
    # 通过别名找到对应的url,然后args会将数字传参给该url形成路径,然后执行相应的视图函数
    def get_list_url(self):
        _url = reverse("%s_%s_list" % self.app_model) # 通过别名反向解析出来的路径:/stark/app01/book/add
        return _url
    def get_add_url(self):
        _url = reverse("%s_%s_add" % self.app_model) # /stark/app01/book/
        return _url
    def get_edit_url(self, obj):
        _url = reverse("%s_%s_change" % self.app_model, args=(obj.pk,))  # /stark/app01/book/2/change/
        return _url
    def get_delete_url(self, obj):
        _url = reverse("%s_%s_delete" % self.app_model, args=(obj.pk,)) # args传参给反向解析出来的url路径,
        return _url

    # urls二级分发, 使用反向解析来做url
    def get_urls(self):
        temp = [
            path('',self.list_view,name="%s_%s_list" % self.app_model ), # 设置别名做反向解析,其他地方,根据这个别名就能找到这个路径
            path('add/',self.add_view,name="%s_%s_add" % self.app_model),
            re_path('(\d+)/change/',self.change_view,name="%s_%s_change" % self.app_model),
            re_path('(\d+)/delete/',self.delete_view,name="%s_%s_delete" % self.app_model),
        ]
        return temp

    # 将一个方法变成一个属性
    @property
    def urls(self):
        return self.get_urls(), None, None



class StarkSite():
    '''
    # StarkSite: 基本类----这里设置一级分发
    # model: 注册模型类
    # ModelStark: 注册模型类的配置类---这里设置二级分发,并设置别名用于反向解析
    '''

    def __init__(self):  # 定义这个字典在url二级分发时体现出来作用,
        self._registry = {} # model_class class -> admin_class instance

    def register(self,model,stark_class=None): # 传参,注册模型类和对应的配置类,这里传的都是类名,所以在调用方法的时候,实际上是调用函数,所以要传self
        stark_class = stark_class or ModelStark  # 若自定制有子类配置类对象则用传入的子类的,没有用默认父类的
        self._registry[model] = stark_class(model) #  该模型类为键,该模型了的配置类为值


    def get_urls(self):
        temp = []
        for model, config_obj in self._registry.items():  # {Book:Bookconfig(Book),Publish:ModelStark(Publish)}
            model_name = model._meta.model_name  # 拿到该表的小写表名
            app_label = model._meta.app_label  # 拿到该表所在的APP名称
            temp.append(
                path('%s/%s/' % (app_label, model_name),config_obj.urls) # 对象直接点该属性,这是一个方法封装的属性
            )
            '''
            # 1 path('app01/book/',Bookconfig(Book).urls),

            path('app01/book/', Bookconfig(Book).list_view),
            path('app01/book/add', Bookconfig(Book).add_view),
            path('app01/book/(\d+)/change/', Bookconfig(Book).change_view),
            path('app01/book/(\d+)/delete/', Bookconfig(Book).delete_view),
            '''

        return temp

    @property
    def urls(self):
        return self.get_urls(), None, None


site=StarkSite()

二、list_display_links

提供用户自定义编辑字段的接口,有就显示用户的,没有就显示默认
class ModelStark():  # 配置类
    list_display_links = [] # 让用户可以自定义点击编辑的字段,没有自定义显示默认的编辑按钮

    # 让所有的查看页面都默认带上这三列选项,选择,删除,编辑
    def get_new_list_display(self):
        new_list_display=[]  # 注意这要用新列表接收
        new_list_display.extend(self.list_display) # 将当前的要显示的列表遍历添加到这个新列表中
        new_list_display.insert(0,ModelStark._checkbox) # 在第一个位置插入选择列
        if not self.list_display_links: # 判断用户有自定义编辑的字段,就不显示默认的编辑按钮了
            new_list_display.append(ModelStark._edit)  #
        new_list_display.append(ModelStark._delete) # 默认添加的 删除功能列的按钮
        return new_list_display

    def _edit(self,obj=None,is_header=False): # obj是当前表记录的对象

        if is_header:
            return "编辑"
        return mark_safe("<a href='%s'>编辑</a>" % self.get_edit_url(obj))

三、构建添加按钮

class ModelStark():  # 配置类
    def list_view(self,request):
        # 为添加按钮获取模型变量信息
        table_name = self.model._meta.verbose_name  # 获取表名
        add_url = self.get_add_url() # 获取当前添加的路径

        return render(request, "stark/list_view.html", locals())

# 通过render渲染出来的数据,交给前端
<div class="panel-heading"><a href="{{ add_url }}" class="btn btn-primary">添加{{ table_name }}</a></div>

四、forms组件的choiceField字段

设置默认的modelform,用于校验数据、获取、传递;
并在此提供接口,可以让用户自定modelform
相关代码示例
app01.models.py文件

class Book(models.Model):
    state=models.IntegerField(choices=((1,"已出版"),(2,"未出版")) ,default=1)
    # choice=((),()),元组里面套元组,类似这种形式,# 1是存在数据里面的值,后面是显示在页面上的已出版或未出版

stark组件,sites.py文件,配置类

class ModelStark():  # 配置类
    model_form_class = None  # 提供用户的自定义modelform接口,

    # 设置默认的modelform,用于校验数据、获取、传递;并在此提供接口,可以让用户自定modelform
    def get_model_form_class(self):
        class BaseModelForm(forms.ModelForm):
            class Meta:
                model = self.model # 得到动态数据表对象
                fields = "__all__"  # 显示所有的字段

        return self.model_form_class or BaseModelForm #用户有自定义,用用户的,没有用默认的

# 用户app01.stark.py文件

from stark.service.sites import site,ModelStark
from . import models

from django import forms
class BookModelFormm(forms.ModelForm):
    class Meta:
        model=models.Book
        fields="__all__"
        error_messages={
            "title":{"required":"该字段不能为空"}
        }

class BookConfig(ModelStark):
    model_form_class = BookModelFormm

site.register(models.Book,BookConfig)

五、modelform的应用

1、先从form组件学习,进而对比学习modelform

forms用法:
# Create your models here.
class Book(models.Model):
    title = models.CharField( max_length=32)
    pub_date=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2)
    state=models.IntegerField(choices=((1,"已出版"),(2,"未出版")),default=1)
    publish=models.ForeignKey(to="Publish",to_field="id",on_delete=models.CASCADE,null=True)
    authors=models.ManyToManyField("Author",db_table="book2authors") # 创建关系表
    def __str__(self):
        return self.title


# forms组件:
from django import forms

class BookForm(forms.Form):
    title=forms.CharField(max_length=32)
    price=forms.DecimalField(max_digits=5,decimal_places=2)
    pub_date=forms.DateField()
    state=forms.ChoiceField(choices=((1,"已出版"),(2,"未出版")))  # 最常用,单选
    # publish=forms.ChoiceField(choices=list(models.Publish.objects.all().values_list("pk","name"))) #

    publish=forms.ModelChoiceField(queryset=models.Publish.objects.all())  # 单选,以主键为键,以对象为文本值,取到的是一个个publish对象

    authors=forms.ModelMultipleChoiceField(queryset=models.Author.objects.all()) # 多选,以主键为键,以对象为文本值


# form 组件自定义属性的方法:

from django.forms import widgets

class EmpForm(forms.Form):
    name=forms.CharField(min_length=5,
                         label="姓名",
                         error_messages={"required":"该字段不能为空!"},
                         widget=widgets.TextInput(attrs={"class":"form-control","id":"yuan"}) # 这样就可以自定属性了,
                         )
    age=forms.IntegerField(label="年龄",
                         widget=widgets.PasswordInput(attrs={"class": "form-control", "id": "yuan"})
                           )

六、modelform的参数设置

1、modelForm的参数及语法

# modelForm # 由model延伸出来的form,这个form可以对model进行操作
# 依赖于模型类转换出来的form,不再需要我们自己写,有模型类直接转换而来

from django import forms

class BookModelForm(forms.ModelForm):  # 写在视图里
    class Meta:
        model=models.Book # 这里就知道是对那个表进行操作

        # fields=["title","price"] # 显示多少个字段
        # exclude = ["title"] # 除了这里的字段,其余的都显示
        fields="__all__"  # 显示所有字段

2、在视图函数中对modelform的使用

def addbook(request):
    if request.method=="POST":
        '''
        对比普通的来看
            data=request.POST.dict()
            data.pop("csrfmiddlewaretoken")
            data.pop("author_list")
            book=models.Book.objects.create(**data)  #  保证提交键值对的键必须和数据库表字段一致
            #  为书籍绑定作者关系
            author_list=request.POST.getlist("author_list")
            print(author_list) # ['1', '2']
            book.authors.add(*author_list)

        '''

        form=BookModelForm(request.POST) # 拿到添加的数据
        if form.is_valid(): # 进行数据校验
            form.save() # 校验通过,直接保存到数据库,这样是添加的操作
            return redirect("/books/") # 重定向到显示页面
        else:
            return render(request, 'addbook.html', locals()) # 校验不通过,带着输入的数据返回添加页面,提示错误信息


    else:

        # form=BookForm()   # forms组件
        form=BookModelForm()       #  modelforms组件
        return render(request,'addbook.html',locals())

3、基于modelform对视图函数进行的编辑操作

def editbook(request,edit_book_id):
        edit_book = models.Book.objects.filter(pk=edit_book_id).first()
        if request.method=="POST":

            # 方式3
            form = BookModelForm(request.POST,instance=edit_book) # 编辑一定要加上instance ,不然form.save()就是新增,不是编辑修改
            # 要搞懂instance的参数edit_book是什么????book就是你要编辑的对象(就是相应的表记录),然后对其进行更新,

            if form.is_valid():  # 开始校验
                form.save()
                return redirect("/books/")
            else:
                return render(request, 'editbook.html', locals())

        else:

            form=BookModelForm(instance=edit_book)
            return render(request,'editbook.html',locals())

4、modelform组件自定义属性、错误信息

ModelForm组件会去找到Models这个模块下的Book表(该类),根据字段去创建检验或渲染的字段,字段我们可以自定义
from django import forms # forms组件
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError

from django.forms import widgets

class BookModelForm(forms.ModelForm):
    class Meta:
        model=models.Book  # 这里就是确定你要找model的对象,
        # fields=["title","price"]  # 根据这里的字段进行显示
        # exclude = ["title"] # 除了这个列表之外的字段显示
        fields="__all__"  # 这个是对所有字段进行显示

        widgets={  # 为字段创建属性,添加bootstrap样式
            "title":wid.TextInput(attrs={"class":"form-control"}),
            "price":wid.TextInput(attrs={"class":"form-control"})
        }
        error_messages={  # 自定义对应的错误的提示信息
            "title":{"required":"该字段不能为空"}
        }
        labels={  # 显示中文
            "title":"书籍名称"
        }

    def clean_title(self):  # 自定义钩子
        val=self.cleaned_data.get("title")
        if val.startswith("yuan"):
            return val
        else:
            raise ValidationError("必须以yuan开头!")

七、stark组件基于modelform实现添加功能

八、render渲染补充知识点:

注意这些代码虽然写在HTML文件中,但并不是前端代码,这是Django才有的render渲染,
前端并不认识这些什么{{ }},{% %}的代码,
都是先经过render渲染后得到结果,交给前端

1、include引入用命令

include命令,通过标签引用,共同的HTML部分

{% include "form.html" %}  {# 引入共同HTML模块部分 #}

2、extend继承命令

{% extends "stark/base.html" %} # 使用同一套模板,直接继承就行啦

3、调用函数,拿到函数的返回值,再经渲染成标签,最后返回给调用的模板

base.html文件

{#   渲染左侧菜单栏   #}
<div class="left-menu">
     {% block side_bar %}
      {% load rbac %}    {#   自定义标签的py文件,根据每一个app从上往下找rbac.py文件  #}

      {% get_menu request %}  # get_menu为函数,request为传的参数

     {% endblock side_bar %}
</div>

rbac.py文件

inclusion_tag  可传参的自定义标签
# 自定义标签,拿下面函数的返回值,交给menu.html文件渲染,再将渲染后的结果返回给调用的HTML文件
# 根据每一个app从上往下找rbac.py文件,前面的rabc是文件夹的名字,纯粹是为了防止同名,而执行的menu.html文件不正确

@register.inclusion_tag("rbac/menu.html")
def get_menu(request):
    print("好的最终数据结果", permission_tree_list)

    import json
    return {"default_data": json.dumps(permission_tree_list)}

# 对比普通的自定义标签,这里不可以传参,单纯的返回一个结果
# 乘法过滤器
@register.simple_tag  # 自定义标签
def multi(x,y):
    return x*y

0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *