• django进阶-modelform&admin action


    先看效果图:

    登陆admin后的界面:

    查看作者:

    当然你也可以定制admin, 使界面更牛逼

    数据库表结构: app01/models.py

     1 from django.db import models
     2 from django.utils.html import format_html  #把字符串变成html
     3 
     4 # Create your models here.
     5 class Author(models.Model):
     6     first_name = models.CharField(max_length=32)
     7     last_name = models.CharField(max_length=32)
     8     email = models.EmailField()
     9 
    10     def __str__(self):
    11         return "<%s %s>" % (self.first_name,self.last_name)
    12 
    13     class Meta:
    14         verbose_name_plural = u"作者"
    15 
    16 class Publisher(models.Model):
    17     name = models.CharField(max_length=64, unique=True)
    18     address = models.CharField(max_length=128,null=True,blank=True)
    19     city = models.CharField(max_length=64)
    20     state_province = models.CharField(max_length=64,help_text="put your province here",verbose_name=u"所属省")
    21     country = models.CharField(max_length=64,editable=False)
    22     website = models.URLField()
    23 
    24     def __str__(self):
    25         return "<%s>" % (self.name)
    26 
    27 class Book(models.Model):
    28     name = models.CharField(max_length=128)
    29     authors = models.ManyToManyField(Author)   #书多对多到作者
    30     publisher = models.ForeignKey(Publisher)  #一对多
    31     publish_date = models.DateField()        #精确到天DateField()
    32     status_choices = (("published", u"已出版"),
    33                       ("producing", u"待出版"),
    34                       ("forbidden", u"禁书"),
    35                       )
    36     # 此时应该给个默认值,给之前没有status字段的书。
    37     status = models.CharField(choices=status_choices, max_length=32, default="producing")
    38 
    39     def __str__(self):
    40         return "<%s %s>" % (self.name,self.publisher)
    41 
    42 
    43     def colored_status(self):
    44         if self.status == "published":
    45             format_td = format_html("<span style='padding:2px;background-color:yellowgreen'>已出版</span>")
    46         elif self.status == "producing":
    47             format_td = format_html("<span style='padding:2px;background-color:red'>待出版</span>")
    48         elif self.status == "forbidden":
    49             format_td = format_html("<span style='padding:2px;background-color:yellow'>禁书</span>")
    50 
    51         return format_td
    52 
    53     colored_status.short_description = "STATUS"  #页面显示为colored_status即方法名,这里改名为STATUS

    现在你看到我直接贴代码,肯定不爽。好吧……

    首先上面有三张表: Author(作者表)、Publisher(出版社表)、Book(图书表)。表与表的关联也很简单,一本书可由多个作者联合出,一个作者可出多本书,书与作者为ManyToMany关系;一本书只能由一个出版社出版,一个出版社可出多本书,一对多,用外键ForeignKey关联。

    其它的__str__是啥?verbose_name是毛线东西? 看我之前的博客咯。

     

    你登陆admin后其实是看不到3张表的,需要在admin.py注册一下:

    1 from app01 import models
    2 
    3 admin.site.register(models.Author)
    4 admin.site.register(models.Book, BookAdmin)
    5 admin.site.register(models.Publisher)

    注册后再刷新页面,便可以看见表了。

    一、form表单定制

    django进阶-1已经有下面这张图了,当你输入为空时,django会出现下面的error提示。

    但现在你前端玩得很6,你觉得django自己的form表单so uglily,你想自己做个form表单(可用bootstrap美化),但又希望你做的表单有django的验证功能(重要!!)。so, How to do it?? 下面来自定制创建图书的form表单。

    第一步: 创建form类

    你定制的form表单可能有很多,应该建一个forms.py文件来存放定制的表单

    接下来在forms.py导入forms, 创建BookForm类(也可以命名为别的~)

    1 from django import forms
    2 from app01 import models
    3 
    4 class BookForm(forms.Form):
    5     name = forms.CharField(max_length=10)
    6     # publisher_id = forms.IntegerField(widget=forms.Select)
    7     publish_date = forms.DateField()

    第二步: 处理用户请求

    用户最开始请求数据是GET方式, 当输入数据后修改数据(创建图书),提交方式为POST

     1 def book_form(request):
     2     form = forms.BookForm()  # 生成form实例
     3     if request.method == "POST":
     4         print(request.POST)
     5         form = forms.BookForm(request.POST)
     6         if form.is_valid():   #进行验证
     7             print("form is ok")
     8             print(form)
     9             print(form.cleaned_data) #{'name': 'qq', 'publish_date': datetime.date(2017, 3, 14)}
    10             form_data = form.cleaned_data
    11             form_data["publisher_id"] = request.POST.get("publisher_id")
    12             book_obj = models.Book(**form_data)  #form_data为字典形式
    13             book_obj.save()
    14         else:
    15             print(form.errors)
    16     publisher_list = models.Publisher.objects.all()
    17 
    18 
    19     return render(request, "app01/book_form.html", {"book_form":form,
    20                                                     "publishers":publisher_list})

    当用户发起GET请求时,要生成一个空的form实例,并从数据库中查找所有的出版社publisher_list,之后进行渲染render. 接下来当然得写book_form.html啦。

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <title>Title</title>
     6 </head>
     7 <body>
     8     <form action="" method="POST">{% csrf_token %}
     9         {{ book_form }}
    10         <select name="publisher_id">
    11             {% for publisher in publishers %}
    12                 <option value="{{ publisher.id }}">{{ publisher.name }}</option>
    13             {% endfor %}
    14         </select>
    15         <input type="submit" value="创建图书"/>
    16     </form>
    17 </body>
    18 </html>

    要是上面看不懂了,就别往下看了……可以看下我之前的博客哈。根据上面的html,会出现如下界面:

    当你输入书名等信息,再点创建图书时,此时是以POST方式发起请求。

    我们要将数据发给django强大的form表单进行验证(你可能没有输入或者日期输入为SB), 这些属于前端的验证,前端的验证是为了减轻后台服务器的压力。只需将请求的数据request.POST传给forms.py的BookForm()即可。

    form = forms.BookForm(request.POST)
    

    此时我们还需要进行后台验证,有可能成功,also可能失败。当验证成功时,form.is_valid返回true.

    if form.is_valid():   #进行验证
    

    验证成功后,自然是获取数据,存到数据库创建图书啦。好,信息是封装在form实例的,现打印form:

    Print(form)
    
    <tr><th><label for="id_name">Name:</label></th><td><input id="id_name" maxlength="10" name="name" type="text"
     value="zcl_papa" required /></td></tr>
    <tr><th><label for="id_publish_date">Publish date:</label></th><td><input id="id_publish_date" name="publish_
    date" type="text" value="2017-03-14" required /></td></tr>
    View Code

    擦,一堆html

    要获取干净的数据,需处理一下:

    form_data = form.cleaned_data
    
    1 Print(form.cleaned_data)
    2 
    3 {'name': 'zcl_papa', 'publish_date': datetime.date(2017, 3, 14)}
    View Code

    form表单定制ending.

    这里有个问题,为啥要在BookForm去掉publisher_id字段??

    因为无法直接获取出版社的信息(下拉列表为空的),要想在前端界面有供选择出版社的input标签,需要自己在html添加

    1         <select name="publisher_id">
    2             {% for publisher in publishers %}
    3                 <option value="{{ publisher.id }}">{{ publisher.name }}</option>
    4             {% endfor %}
    5         </select>
    View Code

    最后还需要将获得的信息加到字典(干净的数据)中,再添加到数据库。
    呵呵,这比较麻烦,下面介绍更优化的方法。

    modelform

    为了解决前面无法获取publisher_id的问题,引入modelform

    modelform与form有很多相似之处的。接下来用modelform做个前端表单,当然,会丑点……

    第一步: 

    在forms.py创建类,继承forms.ModelForm:

    绑定Book表,Book表有什么字段,form表单就有什么字段,当然你也可以用fields使想显示的字段显示在界面

     1 class BookModelForm(forms.ModelForm):
     2 
     3     class Meta:
     4         model = models.Book  #绑定Book类
     5         #fields = {"name", "publish_date"}  #只包括name,publish-date字段
     6         exclude = ()  #代表所有字段都包括
     7         widgets = {
     8             # 可以定义css样式,牛逼啊
     9             "name": forms.TextInput(attrs={"class":"form-control"}),
    10         }

    第二步: 处理用户请求

     1 def book_modelform(request):
     2     form = forms.BookModelForm
     3     print(request.POST)
     4     if request.method == "POST":
     5         form = forms.BookModelForm(request.POST)
     6         if form.is_valid():  # 进行验证
     7             print("form is ok")
     8             print(form)
     9             print(form.cleaned_data)  # {'name': 'qq', 'publish_date': datetime.date(2017, 3, 14)}
    10             form.save()
    11 
    12     return render(request, "app01/book_modelform.html", {"book_form":form})

    第三步: 写html模版,book_modelform.html:

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <title>Title</title>
     6     <style>
     7         .form-control{
     8             color: red;
     9         }
    10     </style>
    11 </head>
    12 <body>
    13     <form action="" method="post">{% csrf_token %}
    14         {{ book_form }}
    15         <input type="submit" value="创建新书"/>
    16     </form>
    17 
    18 </body>
    19 </html>

    效果图:

    app01/forms.py

     1 from django import forms
     2 from app01 import models
     3 
     4 class BookForm(forms.Form):
     5     name = forms.CharField(max_length=10)
     6     # publisher_id = forms.IntegerField(widget=forms.Select)
     7     publish_date = forms.DateField()
     8 
     9 
    10 class BookModelForm(forms.ModelForm):
    11 
    12     class Meta:
    13         model = models.Book  #绑定Book类
    14         #fields = {"name", "publish_date"}  #只包括name,publish-date字段
    15         exclude = ()  #代表所有字段都包括
    16         widgets = {
    17             # 可以定义css样式,牛逼啊
    18             "name": forms.TextInput(attrs={"class":"form-control"}),
    19         }
    View Code

    二、admin定制

    费话不多说,先上效果图:

    第一次看到这界面效果,卧槽,amazing....

    django的admin后台可以定制成上面的形式,当然你也可以自己定制得更漂亮。

    在admin.py加上一个继承admin.ModelAdmin的类

     1 class BookAdmin(admin.ModelAdmin):  #定制book,需将类当作参数传给admin
     2     # list_display不能显示多对多的,比如authors:
     3     # (admin.E109) The value of 'list_display[2]' must not be a ManyToManyField.
     4     list_display = ("id","name", "publisher","publish_date","colored_status","status")  #定制为三列
     5 
     6     search_fields = ("name", "publisher__name")   #__表示出版社关联到名字
     7     list_filter = ("publisher", "publish_date")  #过滤
     8     list_editable = ("name", "publish_date")     #可修改
     9     list_per_page = 10   #每页显示数量
    10     #即便只有一个值也要加逗号,不然就不当成元组了
    11     #The value of 'filter_horizontal' must be a list or tuple.
    12     filter_horizontal = ("authors",)   #选择作者时进行定制 PS:用于多对多关联
    13     raw_id_fields = ("publisher",)    #选择出版社时进行定制 PS:用于FK外键关联
    14 
    15     actions = [make_forbidden]

    定制后完,需将类当作参数传给admin:

    admin.site.register(models.Book, BookAdmin)
    

    定制作者与出版社的效果图:

    ok, 现在有个新需求,想在book界面有个可以选择书出版或未出版的功能,如何实现呢? 而且可以给界面的已出版/未出版加上颜色样式?? 

    首先你要给book表加上一个新的字段:

    1     status_choices = (("published", u"已出版"),
    2                       ("producing", u"待出版"),
    3                       ("forbidden", u"禁书"),
    4                       )
    5     # 此时应该给个默认值,给之前没有status字段的书。
    6     status = models.CharField(choices=status_choices, max_length=32, default="producing")
    7 
    8     def __str__(self):
    9         return "<%s %s>" % (self.name,self.publisher)

    再将status字段加到list_display,就能显示在界面上了。so easy.

    如何实现上图的颜色呢??

    在models.py下的Book类(用于创建Book表),定义colored_status方法(也可定义为别的名~) 加入下面代码:

     1     def colored_status(self):
     2         if self.status == "published":
     3             format_td = format_html("<span style='padding:2px;background-color:yellowgreen'>已出版</span>")
     4         elif self.status == "producing":
     5             format_td = format_html("<span style='padding:2px;background-color:red'>待出版</span>")
     6         elif self.status == "forbidden":
     7             format_td = format_html("<span style='padding:2px;background-color:yellow'>禁书</span>")
     8 
     9         return format_td
    10 
    11     colored_status.short_description = "STATUS"  #页面默认显示为colored_status即方法名,这里改名为STATUS

    自我感觉注释得挺明白的。对不同的status判断,并返回不同的css样式。

    这里注意导入format_html,可将字符串变成对应html

    from django.utils.html import format_html     #把字符串变成html
    

      

    admin action

    需求又来了……

    批量修改书的status,如将选中所有的书全都修改为禁书

    上图的Action下拉框默认只有Delete selected books,如何才能使下拉框能Set to forbidden选项?? 实现选择forbidden再点击Go,便可将选中的书的status改为禁书。

    第一步: 在admin.py定义如下方法,方法名你随意。

    要使界面下拉框有Set to forbidden选项,可用第5句代码:

    1 def make_forbidden(modelAdmin, request, queryset):  #queryset:选中的集合;modelAdmin代表BookAdmin类,相当于self
    2     print("----->", modelAdmin,request,queryset)
    3     queryset.update(status="forbidden")  #更改选中的为禁书
    4     #使action框有选项set to forbidden
    5     make_forbidden.short_description = "Set to forbidden"

    第二步: 在之前定义的BookAdmin类加上actions=[xx], xx为第一步定义的方法名make_forbidden.

    class BookAdmin(admin.ModelAdmin):   #在类中加入下面一句代码.
    	actions=[make_forbidden]
    

    admin.py

     1 from django.contrib import admin
     2 
     3 # Register your models here.
     4 from app01 import models
     5 
     6 
     7 def make_forbidden(modelAdmin, request, queryset):  #queryset:选中的集合;modelAdmin代表BookAdmin类,相当于self
     8     print("----->", modelAdmin,request,queryset)
     9     queryset.update(status="forbidden")  #更改选中的为禁书
    10     #使action框有选项set to forbidden
    11     make_forbidden.short_description = "Set to forbidden"
    12 
    13 
    14 class BookAdmin(admin.ModelAdmin):  #定制book,需将类当作参数传给admin
    15     # list_display不能显示多对多的,比如authors:
    16     # (admin.E109) The value of 'list_display[2]' must not be a ManyToManyField.
    17     list_display = ("id","name", "publisher","publish_date","colored_status","status")  #定制为三列
    18 
    19     search_fields = ("name", "publisher__name")   #__表示出版社关联到名字
    20     list_filter = ("publisher", "publish_date")  #过滤
    21     list_editable = ("name", "publish_date")     #可修改
    22     list_per_page = 10   #每页显示数量
    23     #即便只有一个值也要加逗号,不然就不当成元组了
    24     #The value of 'filter_horizontal' must be a list or tuple.
    25     filter_horizontal = ("authors",)   #选择作者时进行定制 PS:用于多对多关联
    26     raw_id_fields = ("publisher",)    #选择出版社时进行定制 PS:用于FK外键关联
    27 
    28     actions = [make_forbidden]
    29 
    30 
    31 admin.site.register(models.Author)
    32 admin.site.register(models.Book, BookAdmin)
    33 admin.site.register(models.Publisher)
    View Code

    可别小看admin action, 比如你可以批量选中很多主机,点击action发送指令,发送文件  and so on. 强!!!

    转发注明出处: http://www.cnblogs.com/0zcl/p/6580279.html 

  • 相关阅读:
    Snapshot--使用脚本创建快照
    Mirror--日志流压缩
    Mirror--如何TSQL查看镜像状态和镜像相关存储过程
    Mirror--如何对运行中的镜像端点更换证书
    Mirror--程序访问镜像数据库的超时机制
    oozie客户端常用操作命令
    oracle数据库表空间的创建与使用
    Idea创建sbt项目
    Idea配置sbt(window环境)
    使用Idea从github上获取项目
  • 原文地址:https://www.cnblogs.com/0zcl/p/6580279.html
Copyright © 2020-2023  润新知