django学习 -- 可以感受到最原始的django框架是前后端不分离的 -- part.1
1.请求和响应
- get post redirect
2.数据库操作
- mysql+pymsql
- ORM框架 -> 帮助我们处理sql语句 => pip install mysqlclient
 创建修改删除数据中的表(不用写sql语句)
 操作表中的数据
 => 创建数据库 django连接数据
 PS. 需要在setting.py进行设置
3.让django帮助创建数据库表
- django会通过model中的类帮助生成sql创建表和字段
 类名代表表明 属性代表列名
class UserInfo(models.Model):
    name = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
4.执行命令
- 需要提前注册app -> 在setting.py中注册
 python manage.py makemigrations
 python manage.py migrate
5.需要注意
- 在已经有的model类中添加属性 可能会导致原来已经有的数据缺失新增加的列的数据
 django给出了两个选择,一是在命令行中默认添加输入,二是在新增加的属性字段中增加default=字段,代表给原来没有的缺失值赋值。例如:
 age = models.CharField(defalut=18)
 或者设置可以为空
 age = models.CharField(null=True,blank=True)
6.通过django向表中插入数据
- 
UserInfo.objects.create(name='cyanine',password="123456")插入数据 需要类中的属性对应
 a = UserInfo.objects.all()获取所有数据 以列表返回,其中每一个元素都是一个对象 通过.属性获取具体的数据a.name,结合.filter()可以进行筛选查询
 .first()获取获取到的第一个对象
- 
删除数据 
 .delete()
 可以添加filter进行筛选 例如UserInfo.objects.filter(name='cyanine').delete()删除所有name为cyanine的数据
 使用.all()选择全部UserInfo.objects.all().delete()删除这张表中的所有数据
- 
更新数据 
 UserInfo.objects.all().updata(password=123456)更新所有的password为123456
 还可以结合.filter()进行筛选
7.django表单如果不写action 那么默认提交就是向当前页面发送一个post请求
8.django中的url命名能避免对url的硬编码,
- 例如修改一个url 的路径如果以前在html采用的是硬编码,直接写对应的会导致后面修改维护极为困难。
 但是如果采用url命名空间,那么修改的url并不会直接影响到,django会通过url的命名访问到,而不是硬编码。{% url 'url_name' other_param%}这个url_name 就是在urls.py文件中的path('url/example', views.function,name = url_name)的name为url从的重命名,这样修改path对应的url,就不用在维护前端的页面了。
9.model添加约束 (数据库的约束)
- 例如员工表和部门表之间的关系,让员工表和部门表关联,通过ForeignKry方法中的to和to_field参数在django中设置。用途是可以方便对输入数据的合法性判断,例如员工不能属于一个不存在的部门,就可以很方便的进行判断。
- 在员工表中
 department = models.ForeignKey(to = 'Department', to_field = 'id')
 这就将员工表和部门表进行了关联,员工的部门只能属于Depatment中的一个;to表示关联的表名,to_field表明关联的表的一个列名字段。要注意的是,django在储存这样的字段时默认命名方式是关联表名_id(例如这个就是depaetment_id)。
 于此对应,在获取的时候,如果直接通过字段/属性获取的话,例如obj.联表名_id,获取到的时储存在这张表中的对应关联表的数字,而不是我们想要的具体指代,因此django规定了如果直接.联表名就可以获取到关联表对应的那一行数据(封装为一个queryobj对象),然后就可以继续通过关联表内的属性进行查找。例如,部门表中一共有两个部门,字段为(id,部门名),内容为(1,主管部)(2,运行部),员工表关联
- 级联删除 置空null
 现在设想一种情况,如果部门表中的一个部门取消掉,那么与之关联的员工应该如何处理呢?常用的就是两种操作:将他们一并删除(这就是级联删除);还有一种就是将他们设置为null。对应的这两种方式在django中需要这样设置:
 department = models.ForeignKey(to = 'Department', to_field = 'id',on_delete=models.CASCADE)级联删除
 department = models.ForeignKey(to = 'Department', to_field = 'id',null=True,blank=True,on_delete=models.SET_NULL)置空 需要注意的是 置空的前提就是这个字段可以为空null = True这些都是django中的操作。
10.性别存储
- model的choice 与数据库无关 是django的特性 可以方便的进行固定的选择储存。例如男女,单独为男女添加一个char字段会较为浪费空间,因此使用1/2这样的数字代替,但是数字不易读,因此django设计出了choice参数。
gender_choice = (
    (1,'男'),
    (2,'女'),
) #注意是元组套元组
gender = models.SmallIntegerField(verbose_name='性别',choices = gender_choice) #verbose_name就是对列名(属性)的注解 可写可不写 写上为好
#SmallIntegerField是小整数字段
- 这样字段的获取:如果直接obj.gender获取的就是数据库中对应的数字,但是django封装了一个判断方法,可以很方便的帮我们判断,然后返回的是我们希望现实的字段:obj.get_gender_display()。其中函数名字组成方式为get_属性名字/字段名字_display()
11.Form和ModelForm
- 11.1 form类 自动生成表单
#views.py
class MyForm(Form):
    user = forms.CharField(widget=forms.Input)
    password = forms.CharField(widget=forms.Input)
    email = forms.CharField(widget=forms.Input)
def user_add(request):
    form = MyForm() #实力化一个form表单类
    return render(request,'user_add.html',{'form':form}
<!-- user_add.html -->
<form method='post'>
{% for i in form %}
{{ i }}
{% endfor %}
<!-- 或者也可以一个一个自己写出来 比如{{form.user}} {{form.password}} -->
</form>
- 11.2 ModelForm组件
- 实现数据库表单字段和前端form表单字段的自动生成
 - 对连接表/外键限制自动生成选择框
- 定义models类中的__str__方法实现对连接表属性的直接展示
- 定义生成表单样式的属性
- {{ obj.label }}可以遍历出在modelform定义字段中的- label参数
 接下来是具体的代码展示 有点太多了...
 记得添加了路由!!!(这个就不展示在代码中了,)
 
#views.py
#根据staff表单生成modelform类
class staffMdoelForm(ModelForm):
    class Meta:
        model = Staff
        fields = ['name','age','gender','phonenumber','create_time']
#添加员工
def staff_add(request):
    form = staffMdoelForm() #实例化modelform类 
    return render(request,'staff_add.html',{
        'form':form
        })
<div style="width:50%;margin-left: 25%;margin-top: 25px;">
    <div class="container" >
        <form method="post">
            <h2>添加新的员工信息</h2>
            {% csrf_token %}
            {% for field in form %}
            <div class="mb-3">
                <label class="form-label">{{field.label}}</label>
                {{ field }}
            </div>
            {% endfor %}
            <button type="submit" class="btn btn-primary">确认添加</button>
        </form>
    </div>    
</div>
#连接表modelform处理(html不用做太多改变)
#在models.py中的从表添加__str__方法 让调用这个类的时候返回的不再是一个queryobject而是__str__定义好的返回值
class Staff(models.Model):
    name = models.CharField(verbose_name='员工姓名',max_length=32)
......
    def __str__(self):
        return self.name
#在views.py中定义modelform类的时候 字段选择直接写定义的而不是在数据库表单中的字段
class userModelForm(ModelForm):
    class Meta:
        model = UserInfo #注 Userinfo表单中的identify是引用了Staff表单中的id作为外键
        fields = ['name','password','identify']
12.重定向
- from django.shortcuts import redirect或者- from django.http import HttpResponseRedirect
 return一个重定向url的实例即可
13.前端url实现删除请求
- 通过前端发送一个带有删除目标信息的GET请求,然后后端获取删除即可,但是这个带有删除目标信息的url该如何组装呢?通过url的组成规律,添加/?id=信息传递,代码如下:
 <a href="/login/manage/delete/?id={{user.0}}">删除</a>#这其中的{{id.0}}通过django的模板语法动态赋予目标的id信息
 对应处理函数(记得注册url)
def delete_user_info(request):
    id = request.GET.get('id')
    UserInfo.objects.filter(id = id).delete()
    return HttpResponseRedirect('/login/manage/')
14.django的模板语法
- 其中最开始的{% extends 'nav.html'%}必须处于子页面的第一个加载位置,意味着如果前面有哪怕{% load static %}也会报错。
15.django正则匹配url
- 这样就不用前端写带有get参数的url,后端也不需要get获取,而是直接作为一个解析好的参数传递给视图进行处理,二是直接及进行请求和解析。同时也要注意,在url中添加的正则顺序不影响在命名url中的参数顺序
- 具体如下:
#html中
<a href="{% url 'edit' user.0 %}">操作...</a>
#url中
path('login/manage/<int:id>/edit/',views.edit_user_info,name = 'edit')
#views中
def edit_user_info(request,id):
    default_userinfo = UserInfo.objects.filter(id = id).first()
    ....
可以看到<int:id>是在/edit/之前的,但是在html的url中这个顺序不影响。同时在views中对应的函数可以直接接收到url中正则匹配到的参数,而不用再通过解析get请求获取。
16.在django的模板中,可以通过obj.get_联表名_display获取到和后端get_联表名_display()的效果,直接返回对照过后的结果而不是对应的关联id。
17.django的模板可以进行的额外操作:
- {{number : add 1}}就是python的- +=1操作,- {{datetime | date :'Y-m-d}}就是python中的- strftime()函数(要注意的是里面的格式不需要带- %)
18.modelform的字段校验函数
- is_valid()检验提交post请求中的form表单中的字段是否为空,具体的字段就是在- modelform中- fields列表定义的那些.
- 同时还可以在modelforms类中进行字段校验 错误的信息会保存在用post请求实例化的modelform中。
class staffMdoelForm(ModelForm):
    phonenumber = forms.CharField(label='电话号码',max_length=11,min_length=11)#进行的字段校验 
    #规定电话号码的长度为11位
    #还可以通过validators参数进行正则筛选
    class Meta:
        model = Staff
        fields = ['name','age','gender','phonenumber','create_time']
    phonenumber = forms.CharField(label='电话号码',max_length=11,min_length=11)
    #规定电话号码的长度为11位
    #还可以通过validators参数进行正则筛选
19.调整返回的提示信息为汉语
- 通过修改setting.py文件中的语言项
#setting.py
# LANGUAGE_CODE = "en-us" 注释掉
LANGUAGE_CODE = "zh-hans" #添加
20.modelform中instance参数
- 通过设置单条数据对表单进行填充,例如:
#views.py中的一个方法
default_userinfo = UserInfo.objects.filter(id = id).first() #根据id在数据库中获取对应的一条数据
userform = userModelForm(instance=default_userinfo) # 通过instance参数设定一个实例化的表单 让他在前端渲染的时候自动在value上显示从数据库中拿到的默认值
- 通过instance参数和data参数可以实现数据库数据的更新
#在views.py中的某个函数
default_userinfo = UserInfo.objects.filter(id = id).first()
userform = userModelForm(data=request.POST,instance=default_userinfo)
if userform.is_valid():
    userform.save()
    return redirect('/login/manage/')
else:
    return render(request,'edit_info.html',{
        'userform': userform,
    })
- 如果想在用户输入以外的字段进行更新,那么在modelform实例化之后通过instacne属性可以修改:
userform = userModelForm(data=request.POST,instance=default_userinfo)
# useform.instance.字段 =  值
21.django查询排序
- obj.objects.all().order_by('属性名')
 如果给属性名前面添加减号 代表逆向(高到低)排序
22.给原有的表中添加新的字段产生错误"django.core.exceptions.ValidationError" error
- 产生原因:大概率是因为追加新的字段设置默认值的时候产生的格式错误,例如datetime格式但是没有设置默认或者默认格式错误,造成这样的原因。
- 解决方法:重置所有的migrations
- 操作:删除migrations文件夹下除了__init__.py文件以外所有的文件,然后重新执行数据库迁移命令:python manage.py makemigrations,python manage.py migrate
- 如果还不成功可能是因为数据库中原本存在的数据导致,将数据库清库(所有表删除),然后在执行即可。
23.django报错'orderForm' object has no attribute 'get'
- 原因是在设置input标签的样式的时候重写的父类方法出错,参数多写了一个self
#错误源码
    def __init__(self,*arg,**kwargs):
        super().__init__(self,*arg,**kwargs)
        for name, field in self.fields.items():
            # print(name,field) #这个是展示 self.fields.items()可以直接拿到定义在Meta中的fields字段
            field.widget.attrs = {'class' : 'form-control'} #为生成的表单input赋予css属性 
- 解决方法 : 去掉super().__init__(self,*arg,**kwargs)这一行中的self即可,正确写法:
 super().__init__(*arg,**kwargs)
- 探究一下为什么super不需要self作为方法的第一个参数:
 因为super()是一个类 reference -- 感觉好多...暂时搁置一下...😖
24.关闭浏览器校验
在form表单添加novalidate
<form method="post" novalidate >
25.关于models中null和black字段
- 需要同时开启,但是数据库更推荐默认值为空值,因此设置default=None而不是null。
 如果设置blank=True但是不开启null=True,那么在插入的时候,如果传回来的数据为空,插入会报错不能为null字段...总之还是很迷惑的报错....但是解决方法还是最好设置两个都为True并且设置默认字段为None。如果为null,那么在数据传入到前端的时候会默认输出null或者none,而不是将那片区域置空,对于前端的用户输入逻辑不太友好。
26.modelforms便捷写法
- 在添加fields的时候,可以用__all__一次性获取全部,exclude = [ 排除的字段 ]排除掉不要的字段。
class orderForm(ModelForm):
    class Meta():
        model = Orderform
        fields = "__all__" #注意要引号
        # exclude = ['字段1','字段二', .... ]
27.后端表单正则校验
- 在modelforms类中声明字段,然后通过validators参数设置正则表达式:
from django.core.validators import RegexValidator #需要导入正则类
class exampleModelForms(forms.ModelForm):
    moblie = forms.CharField(
        label = '手机号码',
        validactors = [RegexValidator(r'^1[3-9]\d{9}$','手机号格式错误')]       
        #校验失败返回后面的提示信息而不是默认的了~ 
    )
28.钩子方法进行字段校验
- 详细的
- 这里摘列出常用的代码结果
class exampleModelForms(forms.ModelForm):
   def clean_字段名(self): #这个需要在一个modelform类中 这个函数名modelform会自动生成对应的方法
           pass 
           name = self.cleaned_data.get('name')
           if name=='admin':
               raise ValidationError('admin是超级管理员,不能注册!')#这个错误会直接扔进该字段的错误类别中:name.errors
           return self.cleaned_data.get('name')
29.规定modelform中不可编辑的部分
在modelform中字段中设置参数disabled= True即可,这样在前端页面渲染出来的这一条字段是不可以更改的。
30.通过modelform判断要添加的数据是否重复
- order_edit_form = orderEditForm().filter(id=你要查询的).exist()返回True/False
 这个方法需要添加在modelform中的钩子方法中。
- 排除自己以外的是否存在重复?(主要是在修改数据的时候)
 通过exclude排除自己后判断:order_edit_form = orderEditForm().exclude(id='自己的id' ).filter(id=你要查询的).exist()返回True/False
总结 编辑和添加的不同
31.查询(根据某个字的一部分进行)
- filter()还支持传入字典:
order_edit_form = orderEditForm().filter(id=你要查询的,other=你要查询的)
#等于
query_dict = { 'id' : 要查询的, 'other' : 要查询的 }
order_edit_form = orderEditForm().filter(**query_dict)
 #需要注意传入字典的时候需要两个**
PS. 通常我们再变量前加一个星号(*)表示这个变量是元组/列表,加两个星号表示这个参数是字典
32.问题记录:modelform不能更新一行数据的部分字段
- 解决方法
 这里的解决方法很多 包括自定义钩子函数对post数据进行清晰、定义一个工厂函数处理每次不同的字段等,不过都太高端了,部分概念我还没有理解清楚😭
- 因此我打算采取最简单的 -- 一个字段一个字段的去update😳。 或者采用dict的模式,处理不变的字段和更新后的字段 组合成为一个更新后的字典然后通过modelform保存..
- 最后发现是我实例化出错了的问题....(真诚的眼瞎😭)
    row_object = Orderform.objects.filter(id = id).first() #注意这里实例化的是一个form类 之前错误的原因是这里也写成了一个modelform类 这就导致下面instance的时候会报错 而且默认不更新的前端数据实际上是会传递回来的 作为表单的默认值
    order_edit_form = orderEditForm(data=request.POST,instance=row_object) 
    #这里实例化的是一个modelforms类
33.django的条件查询
除了固定的等于(=)查询外,还可以通过给字段/属性添加下划线的方式规定范围查询
- 数字方面:gt、gte、lt、lte:大于、大于等于、小于、小于等于。
 list = BookInfo.objects.filter(id__gt=3)代表id大于3的数据
 PS. 不等于可以通过exclude过滤
- 字符串方面:startswith、endswith:以指定值开头或结尾;contains:是否包含。
 list = BookInfo.objects.filter(btitle__endswith='部')查询是否数据中btitle字段以‘部’这个字结尾的一行数据。
- 日期查询: year、month、day、week_day、hour、minute、second:对日期时间类型的属性进行运算。
 list = BookInfo.objects.filter(bpub_date__year=1980)查询1980年发表的图书
 list = BookInfo.objects.filter(bpub_date__gt=date(1990,1,1))查询1980年1月1日后发表的图书。
- 还有很多查询: 例如比较同一行数据两个字段之间的关系(如阅读数量>=评论数量)、与或非查询、聚合等... 具体参考reference或者官网
PS. 1. filter可以连续使用 :list=BookInfo.objects.filter(bread__gt=20).filter(id__lt=3) 2. filter可以传入字典
34.设置get默认值
- request.GET.get('page', '1')获取get请求,如果get没有附带page参数,那么默认是1.
- 
- 学习视频
- 1.请求和响应
- 2.数据库操作
- 3.让django帮助创建数据库表
- 4.执行命令
- 5.需要注意
- 6.通过django向表中插入数据
- 7.django表单如果不写action 那么默认提交就是向当前页面发送一个post请求
- 8.django中的url命名能避免对url的硬编码,
- 9.model添加约束 (数据库的约束)
- 10.性别存储
- 11.Form和ModelForm
- 12.重定向
- 13.前端url实现删除请求
- 14.django的模板语法
- 15.django正则匹配url
- 16.在django的模板中,可以通过obj.get_联表名_display获取到和后端get_联表名_display()的效果,直接返回对照过后的结果而不是对应的关联id。
- 17.django的模板可以进行的额外操作:
- 18.modelform的字段校验函数
- 19.调整返回的提示信息为汉语
- 20.modelform中instance参数
- 21.django查询排序
- 22.给原有的表中添加新的字段产生错误"django.core.exceptions.ValidationError" error
- 23.django报错'orderForm' object has no attribute 'get'
- 24.关闭浏览器校验
- 25.关于models中null和black字段
- 26.modelforms便捷写法
- 27.后端表单正则校验
- 28.钩子方法进行字段校验
- 29.规定modelform中不可编辑的部分
- 30.通过modelform判断要添加的数据是否重复
- 31.查询(根据某个字的一部分进行)
- 32.问题记录:modelform不能更新一行数据的部分字段
- 33.django的条件查询
- 34.设置get默认值
 
 
- 学习视频
