Django

目录

一、创建Django项目工程

二、项目文件目录

三、Django的url部分

四、Django视图

五、Django模板

六、模型

七、Form操作



Django是MVT模式的python的web框架

M:Model层与MVC模式中的Model层功能大致相同

V:view与MVC中C大致相同,用来处理访问

T:template与MVC中的V大致相同,用于返回页面

一、创建Django项目工程

  在终端输入:django-admin  startproject  projectName
  其他常用命令:  

python manage.py runserver ip:port    # 启动一个web服务
python manage.py startapp appname     # 创建一个app项目
python manage.py makemigrations      # 生成数据库迁移文件
python manage.py migrate          #生成迁移
python manage.py createsuperuser      # 创建一个管理员账户,用于登录Django自带后台

二、项目文件目录

Django_第1张图片


    manage.py:一个命令行工具,可以使你用多种方式对Django项目进行交互
    内层的目录:项目的真正的Python包
    _init _.py:一个空文件,它告诉Python这个目录应该被看做一个Python包
    settings.py:项目的配置
    urls.py:项目的URL声明
    wsgi.py:项目与WSGI兼容的Web服务器入口

  创建好工程之后,我们还需要创建一个app,而这个app就是我们想要书写的项目

执行之前我们说过的命令来创建一个应用,在终端执行命令:python manage.py startapp app01,到这里我们便创建了一个名为app01的应用,此时再看整个工程的目录如下:

Django_第2张图片

app01文件夹:我们创建的a

pp的目录,用来存放app的内容

models.py:用来创建一个数据模型,与数据库操作相关,存入或读取数据时用到这个

views.py:用来书写视图函数的文件

migrations文件夹:用来存放数据库生成迁移的文件,当执行python manage.py makemigrations之后,该文件夹下便会生成新的迁移文件,执行python manage.py migrate命令根据新生的文件夹,对数据库表结构进行修改

三、Django的url部分

项目下的urls.py文件的注释
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))

 通过上面这段文字,我们可以知道url可以有多种对应方式,可以直接对应视图函数,可以对应类中的函数,还可以引入额外的url文件的配置

虽然我们可以在工程目录下的urls.py文件书写路由,但是,不利于管理,因此我们可以在每个app内创建一个py文件来创建属于每个app自己的路由

app01/urls.py文件的内容

from django.conf.urls import url
from app01 import views  # 导入app01目录下的views文件

urlpatterns = [
    # url(正则表达式,视图函数,关键字参数,别名)
    url(r'^$', views.index, name="index"),

]

关于name="index":在html页面中通过{% url 'index' %}即可在页面中得到该url,即使正则匹配部分发生改变,也不需要在html页面中修改url地址,减少网页硬编码,不理解也没事,记住这样做就行

此时还需要在项目的mysite/urls.py文件中进行修改

修改如下

from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
    # url(正则表达式,视图函数,关键字参数,别名)
    # django项目自带后台管理的对应url
    url(r'^admin/', admin.site.urls), 
    # 指向app01中的url文件,当匹配到这一条url后将进入app01/urls.py文件中继续进行匹配
    url(r'^', include("app01.urls")) 
]

Django的路由支持正则匹配,

url(r'^year/(\d{4})/(\d{2})$', views.year),
# 可以为传入的参数指定名字
url(r'^(?P(\d+))/(?P(\d+))$', views.year_month),
url(r'^(?P(\d+))/(?P(\d+))/(?P(\d+))$', views.year_month_day, name="ymd"),

对应的视图函数是

def year(request, *args):
    print(*args)
    return HttpResponse("sucess")


def year_month(request, year,month):
    print(year,month)
    return HttpResponse("year_month")

四、Django视图

Django中的视图函数主要是接收浏览器发送过来的数据,进行逻辑处理然后返回用户想要得到的内容

主要写在views.py文件中

views.py


def year(request, *args):
    print(*args)
    return HttpResponse("sucess")


def year_month(request, year,month):
    print(year,month)
    return HttpResponse("year_month")


def year_month_day(request, *args, **kwargs):
    print(args)
    print(kwargs)
    return HttpResponse("year_month_day")

views.py中的函数必须拥有返回值,并且函数的第一个参数必须是request,因为需要处理request请求,后面的便是自定义的参数,自定义的参数可以是位置参数,可以可变长度参数,也可以是键值对形式的字典。

关于视图函数的返回,通常我们可以利用下面这三个函数

 

查看源码可知:
HTTPResponse()返回一个字符串
HttpResponse(HttpResponseBase):
    """
    An HTTP response class with a string as content.

    This content that can be read, appended to or replaced.
    """
render()函数的第一参数必须是request,第二个是我们要返回的模板,context可以是我们要在模板中渲染的数据
render(request, template_name, context=None, content_type=None, status=None, using=None):
    """
    Returns a HttpResponse whose content is filled with the result of calling
    django.template.loader.render_to_string() with the passed arguments.
    """
redirect()重定向函数,常用于页面的重定向,比如登录后自动跳转到个人中心等
redirect(to, *args, **kwargs):
    """
    Returns an HttpResponseRedirect to the appropriate URL for the arguments
    passed.

    The arguments could be:

        * A model: the model's `get_absolute_url()` function will be called.

        * A view name, possibly with arguments: `urls.reverse()` will be used
          to reverse-resolve the name.

        * A URL, which will be used as-is for the redirect location.

    By default issues a temporary redirect; pass permanent=True to issue a
    permanent redirect
    """

通过查看项目文件的urls.py文件,我们可以发现views.py文件中除了直接写函数外,还可以写类来实现相同的功能

五、Django模板

Django中的模板可以说是我们通常说的html文件,但是又和普通的html文件有所不同,因为这里的模板可以被继承,导入

如果视图函数返回一个简单的页面,那我们就可以直接返回页面,比如




    
    index


首页

此时我们可以得到一个首页的页面,通常首页的顶部的导航条在其他页面中也能看到,那我们总不能在每个需要的导航条的页面都写上导航条的代码,这时我们就可以使用模板的继承了。修改后代码如下

首先创建一个base.html,代码如下




    
    {% block title %}
        base
    {% endblock %}


{% block nav %}

导航条

{% endblock %}
{% block content %}

首页

{% endblock %}
{% block footer %} {% endblock %}

将index.html的代码修改如下

{% extends 'base.html' %}



    
    index


{% include 'my.html' %} #另一种方式来实现公共元素的使用
{% block content %} #覆盖base.html中 block名字为content块中的内容
{{ block.super }} # 会保留原block中的内容

index

{% endblock %}

当我们再次访问index.html的页面时,可以看到页面上不仅有index,还有导航条。因为index页面继承了base的元素。

要求:extends继承页面时,继承语句之前不能有任何的html标签。通过block语句我们可以将继承的页面中的block块覆盖掉,当我们想在原block的基础上进行添加时,我们需要在block的中写上{{  block.super }}。

使用继承的同时,也继承了父页面的的布局。总之,想要公用哪部分元素就把这一部分放到block块中。

PS:除此之外,对于公共的内容,我们还可以利用include来引入内容,例如:nav.html文件,我们想要在多个文件中使用的话,我们需要使用{% include 'nav.html' %}来进行使用

模板语言的内容解析:

在模板中我们可以使用循环,判断,列表,字典,过滤器等

首先是循环:

视图函数,通过render返回一个页面,同时返回一个字典,如果不想返回字典的形式,想要返回index函数中所有的变量

可以在字典位置使用local()函数

def index(request):
    m_l = [1, 2, 3, 4, 5, 6]
    return render(request, 'indedx.html', {"my_list": m_l})

模板,将index中对应位置的代码修改如下,再次访问,页面会增加1 2 3 4 5这几个数。

{% block content %}
{% for i in my_list %}
    {{ i }}
{% endfor %}
{% endblock %}

for循环,if判断有开始有结束。语法如下{% for i in k%}{% endfor %};{% if i %}{% endif %}

通过模板的来实现变量的遍历。{{ 要显示的变量 }},除此之外,在for循环中,Django还提供了一遍for循环中可能用到的变量

变量                    作用
forloop.counter		索引从 1 开始算
forloop.counter0	索引从 0 开始算
forloop.revcounter	索引从最大长度到 1
forloop.revcounter0	索引从最大长度到 0
forloop.first		当遍历的元素为第一项时为真
forloop.last		当遍历的元素为最后一项时为真
forloop.parentloop	用在嵌套的 for 循环中,获取上一层 for 循环的 forloop

forloo.xxxx还可以与if语句用。

循环中可能会出现空值,比如字典{"name":""},当循环到这里时,我们可以使用{% empty %}来提示遇到了空值,然后会输出想要的语句

示例

{% block content %}
{% for i in my_dic %}
    {{ i.name }}
{% empty %}
    名字为空
{% endfor %}

对字典per = {"name":"ajune"},取值时不能per["name"],这样取值了,此时需要利用.来取值,例如per.name即可

六、模型

Django中ORM很强大,

对象关系映射(Object Relational Mapping),它的实质就是将关系数据(库)中的业务数据用对象的形式表示出来,并通过面向对象(Object-Oriented)的方式将这些对象组织起来,实现系统业务逻辑的过程。

在ORM过程中最重要的概念是映射(Mapping),通过这种映射可以使业务对象与数据库分离。从面向对象来说,数据库不应该和业务逻辑绑定到一起,ORM则起到这样的分离作用,使数据库层透明,开发人员真正的面向对象。

  • 映射(Mapping) —— 把表结构映射成类
  • 对象 —— 像操作类对象一样,操作数据库里的数据

Django中Model中常用字段,通过这些字段的组合,可以构建出数据库表对应的Model

AutoField(Field)     - int自增列,必须填入参数 primary_key=True;
django中一般默认自动为我们添加自增字段id,并且默认为主键。
如果不想要django自动添加的,那边可以用这个字段来自定义了,一旦自定义,django将不会自动添加。
IntegerField(Field)      - 整数列(有符号的) -2147483648 ~ 2147483647
BooleanField(Field)        - 布尔值类型
NullBooleanField(Field)        - 可以为空的布尔值
CharField(Field)        - 字符类型- 必须提供max_length参数, max_length表示字符长度
TextField(Field)        - 文本类型
EmailField(CharField):     - 字符串类型,Django Admin以及ModelForm中提供验证机制
IPAddressField(Field)  - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
GenericIPAddressField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
        - 参数:
            protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
            unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"
URLField(CharField)   - 字符串类型,Django Admin以及ModelForm中提供验证 URL
SlugField(CharField)   - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
CommaSeparatedIntegerField(CharField) - 字符串类型,格式必须为逗号分割的数字
UUIDField(Field) - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
FilePathField(Field) - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
        - 参数:
                path,                      文件夹路径
                match=None,                正则匹配
                recursive=False,           递归下面的文件夹
                allow_files=True,          允许文件
                allow_folders=False,       允许文件夹
FileField(Field)
        - 字符串,路径保存在数据库,文件上传到指定目录
        - 参数:
            upload_to = ""    上传文件的保存路径
            storage = None    存储组件,默认django.core.files.storage.FileSystemStorage
ImageField(FileField)
        - 字符串,路径保存在数据库,文件上传到指定目录
        - 参数:
            upload_to = ""    上传文件的保存路径
            storage = None    存储组件,默认django.core.files.storage.FileSystemStorage
            width_field=None,   上传图片的高度保存的数据库字段名(字符串)
            height_field=None   上传图片的宽度保存的数据库字段名(字符串)
DateTimeField(DateField) - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
DateField(DateTimeCheckMixin, Field) - 日期格式      YYYY-MM-DD
TimeField(DateTimeCheckMixin, Field) - 时间格式      HH:MM[:ss[.uuuuuu]]

而对于字段,每个字段还拥有一系列的参数

null                数据库中字段是否可以为空
db_column           数据库中字段的列名
db_tablespace
default             数据库中字段的默认值
primary_key        数据库中字段是否为主键
db_index           数据库中字段是否可以建立索引
unique             数据库中字段是否可以建立唯一索引
unique_for_date     数据库中字段【日期】部分是否可以建立唯一索引
unique_for_month   数据库中字段【月】部分是否可以建立唯一索引
unique_for_year     数据库中字段【年】部分是否可以建立唯一索引
verbose_name       Admin中显示的字段名称
blank               Admin中是否允许用户输入为空
editable            Admin中是否可以编辑
help_text           Admin中该字段的提示信息
choices             Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
 如:gf = models.IntegerField(choices=[(0, '李白'),(1, '杜甫'),],default=1)

error_messages      自定义错误信息(字典类型),从而定制想要显示的错误信息;
                        字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date	如:{'null': "不能为空.", 'invalid': '格式错误'}
validators          自定义错误验证(列表类型),从而定制想要的验证规则
from django.core.validators import RegexValidator
from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\                    MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
如:
     test = models.CharField(
              max_length=32,
              error_messages={
              'c1': '优先错信息1',
              'c2': '优先错信息2',
              'c3': '优先错信息3',
          },
     validators=[
              RegexValidator(regex='root_\d+', message='错误了', code='c1'),
              RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'),
              EmailValidator(message='又错误了', code='c3'), ]
       )

class UserInfo(models.Model):
        nid = models.AutoField(primary_key=True)
        username = models.CharField(max_length=32)
        class Meta:
            # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
            db_table = "table_name"

            # 联合索引
            index_together = [
                ("字段1", "字段2"),
            ]

            # 联合唯一索引
            unique_together = (("字段1", "字段2"),)

            # admin中显示的表名称
            verbose_name =”用户信息”

            # verbose_name加s
            verbose_name_plural
        

在Django的Models.py定义表结构,利用python manage.py makemigration生成迁移,再利用python manage.py migrate创建数据表,下面是一个例子

# 书
class Book(models.Model):
    bookName = models.CharField(max_length=20)	#书名
    price = models.IntegerField()		#价格
    publish = models.ForeignKey("Publish")	#出版社
    authors = models.ManyToManyField("Author") 	# 作者

    def __str__(self):
        return self.bookName 	#当打印对象时,返回书名

#出版社
class Publish(models.Model):
    name = models.CharField(max_length=20)	#出版社名字
    city = models.CharField(max_length=20)	#出版社所在城市

    def __str__(self):
        return self.name	#返回出版社名字

#作者
class Author(models.Model):
    name = models.CharField(max_length=12)	#作者名字
    age = models.IntegerField()	                #年龄

    def __str__(self):
        return self.name	#返回作者名字


当生成数据库表后,数据库中会出现下面几张表
应用名_book,应用名_publish,应用名_author,应用名_book_authors

在生成数据库表时,默认名字是应用名_类名,
但是当一个表中出现了ManyToManyField字段时,django会自动生成一个多对多的表,在这里表名为应用名_book_authors的这张表便是自动创建的多对多的表。

当然我们也可以手动的创建一张多对多的表

class Book_Author(models.Model):
    book = models.ForeignKey("Book")
    author = models.ForeignKey("Author")

    class Meta:
        # 联合唯一
        unique_together = [
            ("book", "author")
        ]

如果多对多表中不需要额外的字段,那么为了方便使用,尽量还是使用Django自动为我们生成的表较好。

下面是Django关于数据库的增删改查

增:

'''                                           
添加数据的两种方式,                                    
方式一:                                          
增加一条数据,可以接受字典类型数据 **kwargs                    
modes.类名.object.create(字段1=值1,字段2=值2...)  
还可以直接传入字段和值组成的字典
models.Book.objects.create(**book)    
                                              
                                              
方式二:需要调用save()方法                              
obj = models.类名(字段1=值1,字段2=值2...)   
#models.Book(**book)          
obj.save()                                    
                                              
PS:当添加记录时,如果存在外键,有两种方式                        
方式一:                                          
由于Django的ORM在创建数据库表时,对于表外键,会在外键字段的字段名的后面加上_id,
通过使用字段名_id = 外键id这种方式可以添加                     
方式二:                                          
由于每条记录对应一个对象,外键也是对应一个对象                       
所以可以通过外键字段 = 外键对象的方式进行添加                      
                                              
对于多对多:                                        
对象.多对多字段.add(另一张表中的对象或者是id)                   
eg:                                           
    book_obj.authors.add(author_obj)          
'''                                           

查:重要的一点,通过get方式查询时,当记录不存在或者记录条数超过一条时 ,均会报错

单表查询

'''                                                                                                       
单表查询                                                                                                      
'''                                                                                                       
book_all = models.Book.objects.all()                                                                      
# values(字段1[,字段2,...]),结果返回形式

多表查询:

# 搜索时,通过外键名__外键表中的字段来进行搜索                                                    
# 当要取出值时通过.来获取值                                                              
models.Book.objects.filter(publish__city='上海')                               
                                                                             
'''                                                                          
正向查询                                                                         
主表---》副表----》主表                                                              
                                                                             
通过查询主表中的外键字段,获取对应的对象或值后,通过该对象或者值在附表中查询,得到结果                                  
                                                                             
'''                                                                          
                                                                             
# 查询某个出版社出版的所有书籍                                                             
# 通过对象                                                                       
# 方法一正向查询                                                                    
book_obj = models.Book.objects.get(bookName="GO")                            
pub_obj = book_obj.publish                                                   
book_list = models.Book.objects.filter(publish=pub_obj)                      
print(book_list)                                                             
# 方法二                                                                        
pub_obj = models.Publish.objects.filter(name="人民出版社")[0]                     
book_list = models.Book.objects.filter(publish=pub_obj)                      
print(book_list)                                                             
# 方法三反向查询                                                                    
pub_obj = models.Publish.objects.filter(name="人民出版社")[0]                     
book_list = pub_obj.book_set.all()                                           
print(book_list)                                                             
# 通过外键字段                                                                     
# 通过filter value(双下划线)                                                       
book_list = models.Book.objects.filter(publish__name="人民出版社")                
print(book_list)                                                             
# 查找出版了GO书籍的出版社的信息                                                           
pub_obj = models.Publish.objects.filter(book__bookName="GO")                 
print(pub_obj)                                                               
pub_obj = models.Book.objects.filter(bookName="GO").values("publish__name")  
print(pub_obj)                                                               
book_list = models.Book.objects.filter(publish__city="北京")                   
print(book_list)                                                             

双下划线:

'''                                                                                         
双下划线                                                                                      
'''                                                                                         
# 获取个数                                                                                      
#                                                                                           
models.Book.objects.filter(name='seven').count()                                            
                                                                                            
# 大于,小于                                                                                     
#                                                                                           
models.Book.objects.filter(id__gt=1)              # 获取id大于1的值                               
models.Book.objects.filter(id__gte=1)             # 获取id大于等于1的值                             
models.Book.objects.filter(id__lt=10)             # 获取id小于10的值                              
models.Book.objects.filter(id__lte=10)            # 获取id小于10的值                              
models.Book.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值                        
                                                                                            
# in                                                                                        
#                                                                                           
models.Book.objects.filter(id__in=[11, 22, 33])       # 获取id等于11、22、33的数据                   
models.TBook1.objects.exclude(id__in=[11, 22, 33])    # not in                              
                                                                                            
# isnull                                                                                    
models.Book.objects.filter(pub_date__isnull=True)                                           
                                                                                            
# contains,字段值中含有该值的记录                                                                                  
#                                                                                           
models.Book.objects.filter(name__contains="ven")                                            
models.Book.objects.filter(name__icontains="ven") # icontains大小写不敏感                         
models.Book.objects.exclude(name__icontains="ven")                                          
# 其他类似                                                                                      
# __startswith,以xx开头                                                                        
# __istartswith, 以xx开头,大小写不敏感                                                               
# __endswith, 以xx结尾                                                                         
# __iendswith,以xx结尾,大小写不敏感                                                                  
                                                                                            
# range                                                                                     
#                                                                                           
models.Book.objects.filter(id__range=[1, 9])   # 范围bettwen and                              
                                                                                            
# regex正则匹配,iregex 不区分大小写                                                                   
#                                                                                           
models.Book.objects.get(title__regex=r'^(An?|The) +')                                       
models.Book.objects.get(title__iregex=r'^(an?|the) +')                                      
                                                                                            
# date                                                                                      
#                                                                                           
models.Book.objects.filter(pub_date__date=datetime.date(2005, 1, 1))                        
models.Book.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))                    
                                                                                            
# year                                                                                      
#                                                                                           
models.Book.objects.filter(pub_date__year=2005)                                             
models.Book.objects.filter(pub_date__year__gte=2005)                                        

前面的查询大多是必须符合两个条件的记录,那如何获取一个区间的信息,或者存在两个条件,但只要符合一个条件即可的记录如何查找?

F,Q查询

# F 可以使用模型的字段A与字段B进行比较,如果A写在了等号的左边,则B出现在等号的右边,需要通过F对象构造,即F对象可以使用字段的值经过运算后在给该字段或者其他字段                                                       
#                                                          
from django.db.models import F,Q                           
models.Book.objects.update(num=F('num')+1)                 
                                                           
# Q      
#    过滤器的方法中关键字参数查询,会合并为And进行
#    需要进行or查询,使用Q()对象
#    Q对象(django.db.models.Q)用于封装一组关键字参数,这些关键字参数与“比较运算符”中的相同

#    Q对象可以使用&(and)、|(or)操作符组合起来
#    当操作符应用在两个Q对象时,会产生一个新的Q对象
#    使用~(not)操作符在Q对象前表示取反

#    可以使用&|~结合括号进行分组,构造复杂的Q对象
#    过滤器函数可以传递一个或多个Q对象作为位置参数,如果有多个Q对象,这些参数的逻辑为and
#    过滤器函数可以混合使用Q对象和关键字参数,所有参数都将and在一起,Q对象必须位于关键字参数的前面

#                                                          
# 方式一:                                                     
# Q(id__gt=10)                                             
# Q(id=8) | Q(id__gt=10)                                   
# Q(Q(id=8) | Q(id__gt=10)) & Q(caption='root')            
                                                           
models.Book.objects.filter(Q(id__lt=8) & Q(id__gt=10))     
# 或者是                                                      
# k = Q(id__lt=8) & Q(id__gt=10)                           
models.Book.objects.filter(k)                              
                                                           
# 方式二:                                                     
con = Q()                                                  
q1 = Q()                                                   
q1.connector = 'OR'                                        
q1.children.append(('id', 1))                              
q1.children.append(('id', 10))                             
q1.children.append(('id', 9))                              
q2 = Q()                                                   
q2.connector = 'OR'                                        
q2.children.append(('c1', 1))                              
q2.children.append(('c1', 10))                             
q2.children.append(('c1', 9))                              
con.add(q1, 'AND')                                         
con.add(q2, 'AND')                                         
models.Book.objects.filter(con)                            

 删除:

'''                                                                
删除操作:                                                              
删除操作可以与查找操作配合使用                                                    
查找完成之后使用.delete()方法删除                                              
'''                                                                
# models.Tb1.objects.filter(name='seven').delete() # 删除指定条件的数据     
models.Book.objects.get(bookName="隋唐英雄传").delete()                 
models.Book.objects.filter(bookName="计算机基础").delete()              

改:推荐使用第一种方式

    # 改                                                                                                                                                                                                            
    # 将指定条件的数据更新,均支持 **kwargs,推荐                                                                                                                                                                                   
    models.Book.objects.filter(bookName='GO').update(price=39)                                                                                                                                                     
    '''                                                                                                                                                                                                            
UPDATE `app01_book` SET `price` = 39 WHERE `app01_book`.`bookName` = 'GO'; args=(39, 'GO')                                                                                                                         
                                                                                                                                                                                                                   
    '''                                                                                                                                                                                                            
                                                                                                                                                                                                                   
    # 先获取指定记录,再更新,费时间;因为先把所有的字段值取出,再更新所有的字段值                                                                                                                                                                       
    obj = models.Book.objects.get(bookName='GO')                                                                                                                                                                   
    obj.price = 12                                                                                                                                                                                                 
    obj.save()                                                                                                                                                                                                     
                                                                                                                                                                                                                   
    '''                                                                                                                                                                                                            
SELECT `app01_book`.`id`, `app01_book`.`bookName`, `app01_book`.`price`, `app01_book`.`pub_time`, `app01_book`.`publish_id` FROM `app01_book` WHERE `app01_book`.`bookName` = 'GO'; args=('GO',)                   
UPDATE `app01_book` SET `bookName` = 'GO', `price` = 12, `pub_time` = '2019-01-08 16:00:00', `publish_id` = 15 WHERE `app01_book`.`id` = 1; args=('GO', 12, '2019-01-08 16:00:00', 15, 1)  
    '''                        
                                                                                                                                                                                                                   

七、Form操作

在Django中每当后端获得前端发送的数据时均须要检验数据的合法性,这样一来我们不得不写一大堆的验证规则,这样无疑是非常麻烦的。

因此我们可以使用Django来帮助我们来完成这些操作。

class Myform(forms.Form):
    user = fields.CharField(
        max_length=18,    # 最大长度
        min_length=6,     # 最小长度
        required=True,    # 值不能为空
        label="用户名:",
        initial="ajune",  # 初始化显示
        error_messages={    # 错误类型的提示信息
            "max_length": "太长了",
            "min_length": "太短了",
            "required": "此字段不能为空"
        }
    )
    age = fields.IntegerField(
        required=True,
        label="年龄:",
        # 最大值
        max_value=100,
        # 最小值
        min_value=0,
        error_messages={
            "required": "此字段不能为空",
            "invalid": "年龄必须为数字"
        }
    )
    pwd = fields.CharField(
        required=True,
        min_length=3,
        max_length=8,
        # 过滤空白
        strip=True,
        label="密码:",
        widget=widgets.PasswordInput,
        error_messages={
            "max_length": "太长了",
            "min_length": "太短了",
            "required": "此字段不能为空"
        }
    )
    email = fields.EmailField(
        required=True,
        label="邮箱:",
        error_messages={
            "required": "此字段不能为空",
            "invalid": "邮箱格式不正确"
        }
    )

    # 自定义验证规则
    def mobile_validate(value):
        mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
        if not mobile_re.match(value):
            raise ValidationError('手机号码格式错误')

    phone = fields.CharField(
        label="手机号:",
        # validators=[mobile_validate,],
        validators=[
            RegexValidator(r'^[0-9]+$', '请输入数字'),
            RegexValidator(r'^159[0-9]+$', '数字必须以159开头')
        ],
        # 标签后添加
        label_suffix="->"
    )

在使用时,我们需要实例化类,将对象传到前端。

后端代码:

def modelformtest(request):
    ret = {"status": True, "error": None, "data": None}
    if request.method == "GET":
        # 传入默认值
        # moren = {
        #     "user": 1233123,
        #     "age": 1,
        #     "pwd": 1231233,
        #     "email": "[email protected]"
        # }
        # mf = Myform(mren)
        # 实例化表单类
        mf = Myform()
        return render(request, 'form.html', {"form": mf})
    elif request.method == "POST":
        mf = Myform(request.POST)
        if mf.is_valid():
            print(mf.cleaned_data)
            ret["data"] = mf.cleaned_data
            # 如果form中的字段和数据库中的字段同名,则直接按照下列方法添加
            # models.Book.objects.create(**mf.cleaned_data)
            # 指明向前端发送json数据, content_type="application/json"
            return HttpResponse(json.dumps(ret))
        else:
            print(mf.errors)
            ret['error'] = mf.errors
            return HttpResponse(json.dumps(ret))

前端代码:

label,定义的内容,如果后端没指定将会显示默认值段的值,例如user
form.user生成指定的插件,即html标签,
form.user.errors.0显示错误信息的第一条

{{ form.user.label }}{{ form.user }}{{ form.errors.user.0 }}

{{ form.pwd.label }}{{ form.pwd }}{{ form.errors.pwd.0 }}

{{ form.age.label }}{{ form.age }}{{ form.errors.age.0 }}

{{ form.email.label }}{{ form.email }}{{ form.errors.email.0 }}

{{ form.phone.label }}{{ form.phone }}{{ form.errors.phone.0 }}

这段代码在前端会自动生成指定的html标签,当数据提交到后端时,需要调用先实例化对象mf = Myform(request.POST),将前端发送的数据作为参数传入,然后通过mf.is_valid()来验证数据的合法性。而此时前端的数据将会保存在mf.cleaned_data中,

如果有数据不合法,将会将错误的信息保存到mf.errors中。

当使用Django的Form表单的功能时,每个字段都会存在默认的验证规则用于对数据进行验证,除此之外,还可以通过

validators=[
            RegexValidator(r'^正则表达式1', '信息提示'),
            RegexValidator(r'正则表达式2', '信息提示')
        ],

或者

 # 自定义验证规则
    def mobile_validate(value):
        mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
        if not mobile_re.match(value):
            raise ValidationError('手机号码格式错误')
validators=[mobile_validate,]

自定义函数的方式来编写验证规则。

Django为我们预留了钩子函数在字段验证时,当然这些函数都需要放在定义的表单类中。

# clean_字段名(),必须含有返回值,返回self.cleaned_data["字段名"]
    def clean_user(self):
        v = self.cleaned_data["user"]
        print("验证用户名,当执行表单验证时执行", v)
        if True:
            raise ValidationError("用户已存在")
        return v

    def clean_age(self):
        print("验证年龄,当执行表单验证时执行")
        return self.cleaned_data["age"]

当想要验证多个字段是否同时满足条件时,则需要用到另外的一个钩子函数

    def clean(self):
        v = self.cleaned_data
        user = v.get("user")
        pwd = v.get("pwd")
        if v != "666666" and pwd != "6666666":
            # raise ValidationError("整体错误信息")
            pass
        print("所有字段都验证完成了", user, "\t", pwd)
        # 必须返回 self.cleaned_data
        return v
Field
    required=True,               是否允许为空
    widget=None,                 HTML插件
    label=None,                  用于生成Label标签或显示内容
    initial=None,                初始值
    help_text='',                帮助信息(在标签旁边显示)
    error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
    show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
    validators=[],               自定义验证规则
    localize=False,              是否支持本地化
    disabled=False,              是否可以编辑
    label_suffix=None            Label内容后缀
 
CharField(Field)
    max_length=None,             最大长度
    min_length=None,             最小长度
    strip=True                   是否移除用户输入空白
 
IntegerField(Field)
    max_value=None,              最大值
    min_value=None,              最小值
 
 
DecimalField(IntegerField)
    max_value=None,              最大值
    min_value=None,              最小值
    max_digits=None,             总长度
    decimal_places=None,         小数位长度
 
BaseTemporalField(Field)
    input_formats=None          时间格式化   
 
DateField(BaseTemporalField)    格式:2015-09-01
TimeField(BaseTemporalField)    格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
 

 
RegexField(CharField)
    regex,                      自定制正则表达式
    max_length=None,            最大长度
    min_length=None,            最小长度
    error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}
 
EmailField(CharField)      
 
FileField(Field)
    allow_empty_file=False     是否允许空文件
 
ImageField(FileField)      
    ...
    注:需要PIL模块,pip3 install Pillow
    以上两个字典使用时,需要注意两点:
        - form表单中 enctype="multipart/form-data"
        - view函数中 obj = MyForm(request.POST, request.FILES)

 
ChoiceField(Field)
    ...
    choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
    required=True,             是否必填
    widget=None,               插件,默认select插件
    label=None,                Label内容
    initial=None,              初始值
    help_text='',              帮助提示
 

ModelChoiceField(ChoiceField)
    ...                        django.forms.models.ModelChoiceField
    queryset,                  # 查询数据库中的数据
    empty_label="---------",   # 默认空显示内容
    to_field_name=None,        # HTML中value的值对应的字段
    limit_choices_to=None      # ModelForm中对queryset二次筛选
     
ModelMultipleChoiceField(ModelChoiceField)
django.forms.models.ModelMultipleChoiceField
 
 
     
TypedChoiceField(ChoiceField)
    coerce = lambda val: val   对选中的值进行一次转换
    empty_value= ''            空值的默认值
 
MultipleChoiceField(ChoiceField)
 
TypedMultipleChoiceField(MultipleChoiceField)
    coerce = lambda val: val   对选中的每一个值进行一次转换
    empty_value= ''            空值的默认值
 
ComboField(Field)
    fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
                               fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
 
MultiValueField(Field)
    PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
 
SplitDateTimeField(MultiValueField)
    input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
    input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
 
FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
    path,                      文件夹路径
    match=None,                正则匹配
    recursive=False,           递归下面的文件夹
    allow_files=True,          允许文件
    allow_folders=False,       允许文件夹
    required=True,
    widget=None,
    label=None,
    initial=None,
    help_text=''
 
GenericIPAddressField
    protocol='both',           both,ipv4,ipv6支持的IP格式
    unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
 
SlugField(CharField)           数字,字母,下划线,减号(连字符)
 
UUIDField(CharField)           uuid类型

常用字段及插件

CharField(Field)
IntegerField(Field)
DecimalField(IntegerField)
RegexField(CharField)
EmailField(CharField)       
FileField(Field)
ImageField(FileField)      
ChoiceField(Field)   
MultipleChoiceField(ChoiceField)
GenericIPAddressField


TextInput(Input)
NumberInput(TextInput)
EmailInput(TextInput)
PasswordInput(TextInput)
HiddenInput(TextInput)
Textarea(Widget)
CheckboxInput
Select
SelectMultiple
RadioSelect
CheckboxSelectMultiple
FileInput
SelectDateWidget

关于多选与单选


# 常用选择插件
# 单radio,值为字符串
user = fields.CharField(
    initial=2,
    widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
)

# 单radio,值为字符串
user = fields.ChoiceField(
    choices=((1, '上海'), (2, '北京'),),
    initial=2,
    widget=widgets.RadioSelect
)

# 单select,值为字符串
user = fields.CharField(
    initial=2,
    widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
)

# 单select,值为字符串
user = fields.ChoiceField(
    choices=((1, '上海'), (2, '北京'),),
    initial=2,
    widget=widgets.Select
)

# 多选select,值为列表
user = fields.MultipleChoiceField(
    choices=((1,'上海'),(2,'北京'),),
    initial=[1,],
    widget=widgets.SelectMultiple
)

# 单checkbox
user = fields.CharField(
    widget=widgets.CheckboxInput()
)

# 多选checkbox,值为列表
user = fields.MultipleChoiceField(
    initial=[2, ],
    choices=((1, '上海'), (2, '北京'),),
    widget=widgets.CheckboxSelectMultiple
)

通过初始化方法实时更新选项

def __init__(self, *args, **kwargs):
        # myform为类名
        super(Myform,self).__init__(*args, **kwargs)
        # self.fields['user'].widget.choices = ((1, '上海'), (2, '北京'),)
        # 或
        self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption')

 

你可能感兴趣的:(Django)