python之框架篇(Django)
内容概要:Django项目创建、调配和运行;路由和反向解析;中间件;模板继承、子页面引用;filter函数;CBV和装饰器;request和response;文件上传;
Model和ModelForm,modelformset_factory
Model和ModelForm,modelformset_factory
a、终端模式创建项目(虚拟环境、Windows平台、python3.6):
#1新建虚拟环境 #新建一个文件夹,进入文件夹。shift+右键打开命令窗口 pip install pipenv#此处使用pipenv创建虚拟环境 pipenv install pipenv shell pipenv install django==1.11.18 -i https://pypi.douban.com/simple/ #2、创建项目 django-admin startproject projectname #3、创建app名称 python manage.py startapp appname #4、配置文件 #注册app INSTALLED_APPS = [ 'app01', 'app01.apps.App01Config', ] #配置数据库 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # 引擎 'NAME': 'day60', # 数据库名字 'HOST': '127.0.0.1', # IP 'PORT': 3306, # 端口 'USER': 'root', # 用户名 'PASSWORD': '123', # 密码 } } #模板选择 TEMPLATES 'DIRS': [os.path.join(BASE_DIR, 'templates')] #静态文件配置 STATIC_URL = '/static/' # 别名 STATICFILES_DIRS = [ os.path.join(BASE_DIR,'static'), os.path.join(BASE_DIR,'static1') , ] #执行数据库迁移的命令 python manage.py makemigrations # 将models变更记录保存到migrations python manage.py migrate # 将变更同步到数据库中 #5、书写url、view函数(略)
#6、启动项目 python manage.py runserver #127.0.0.1:8000 #python manage.py runserver 80 #127.0.0.1:80 #python manage.py runserver 0.0.0.0:80 #0.0.0.0:80
b、pycharm创建项目:
file ——》 new project ——》 django ——》 项目目录 ——》选择解释器——》输入app名称 ——》创建
tools ——》 run manage.py task ——》 startapp app名称,或者在上步的more settings中设置app
url(r'rbac/', include('rbac.urls', namespace='rbac')) reverse('rbac:name') from django.conf.urls import url,include from django.contrib import admin from crm import views from crm.views import auth urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/', auth.login,name='login'), url(r'^index/', auth.index,name='index'), url(r'^reg/', auth.reg,name='reg'), url(r'^crm/', include('crm.urls')), url(r'^rbac/', include('rbac.urls',namespace='rbac')), ] #app下url中 from django.conf.urls import url from crm import views from crm.views import customer,auth,consult,enrollment,teacher urlpatterns = [ url(r'^customer_list/', customer.CustomerList.as_view(),name='customer_list'), url(r'^mycustomer/', customer.CustomerList.as_view(),name='mycustomer'), # url(r'^user_list/', views.user_list), url(r'^add_customer/', customer.add_customer,name='add_customer'), # url(r'^add_customer/', customer.customer_change,name='add_customer'), # 编辑客户 url(r'^edit_customer/(\d+)', customer.edit_customer,name='edit_customer'), # url(r'^edit_customer/(\d+)', customer.customer_change,name='edit_customer'), url(r'^consult_list/(0)', consult.ConsultList.as_view(), name='all_consult_list'), url(r'^consult_list/(?P<customer_id>\d+)', consult.ConsultList.as_view(), name='consult_list'), url(r'^add_consult/', consult.add_consult, name='add_consult'), url(r'^edit_consult/(\d+)', consult.edit_consult, name='edit_consult'), url(r'^enrollment_list/', enrollment.EnrollmentList.as_view(), name='enrollment_list'), url(r'^add_enrollment/(?P<customer_id>\d+)', enrollment.enrollment_change, name='add_enrollment'), url(r'^edit_enrollment/(?P<enrollment_id>\d+)', enrollment.enrollment_change, name='edit_enrollment'), #班级展示 url(r'^class_list/', teacher.ClassList.as_view(), name='class_list'), #班级添加与修改 url(r'^add_class/', teacher.class_change, name='add_class'), url(r'^edit_class/(\d+)', teacher.class_change, name='edit_class'), #展示上课记录 url(r'^courserecord_list/(?P<class_id>\d+)', teacher.CourseRecordList.as_view(), name='courserecord_list'), #上课记录添加与修改 url(r'^add_courserecord/(?P<class_id>\d+)', teacher.courserecord_change, name='add_courserecord'), url(r'^edit_courserecord/(?P<courserecord_id>\d+)', teacher.courserecord_change, name='edit_courserecord'), #展示学习记录 url(r'^studyrecord_list/(?P<courserecord_id>\d+)', teacher.studyrecordlist, name='studyrecord_list'), ]
前端:
#settings中url配置 url(r'^add_enrollment/(?P<customer_id>\d+)', enrollment.enrollment_change, name='add_enrollment') #html中反向解析 <a href="{% url 'add_enrollment' customer.pk %}">添加</a> {% load static %} <link rel="stylesheet" href="{% static 'css/reset.css' %}"> <script src={% static "js/supersized.3.2.7.min.js" %}></script> #命名分组的关键字解析方式: url(r'edit_customer/(?P<pk>\d+)/', customer.edit_customer, name='edit_customer') {% url 'edit_customer' pk=2 %} ——》 '/crm/edit_customer/2/'
视图函数中:
from django.shortcuts import redirect, reverse if request.path_info in [reverse('login'), reverse('reg')] or request.path_info.startswith('/admin/'):pass #示例: #settings中url配置 url(r'^courserecord_list/(?P<class_id>\d+)', teacher.CourseRecordList.as_view(), name='courserecord_list') views中反向解析 from django.urls import reverse #return reverse(name,args=args,kwargs=kwargs) reverse('edit_customer',args=(1,)) ——》 '/crm/edit_customer/1/' #命名分组反向解析示例 reverse('edit_customer',kwargs={'pk':'20'}) url(r'edit_customer/(?P<pk>\d+)/', customer.edit_customer, name='edit_customer')
3、django中间件(五种中间件方法、csrf、cookie/session)
a.五种中间件:
#书写中间件类:process_request方法 class AuthenticationMiddleware(MiddlewareMixin): def process_request(self, request): if request.path_info in [reverse('login'), reverse('reg')] or request.path_info.startswith('/admin/'): return pk = request.session.get('pk') obj = models.UserProfile.objects.filter(pk=pk).first() if obj: request.user_obj = obj return return redirect(reverse('login')) #settings中配置中间件 MIDDLEWARE = [ 'crm.middlewares.auth.AuthenticationMiddleware' ]
#process_template_response方法 class Response(MiddlewareMixin): def process_template_response(self, request, response): response.context_data["user"] = request.account return response
b.csrf中间件
#settings中csrf配置 MIDDLEWARE = [ 'django.middleware.csrf.CsrfViewMiddleware', ] #前端网页使用 {% csrf_token %} #views代码中: from django.views.decorators.csrf import ensure_csrf_cookie @ensure_csrf_cookie def login(request): if request.method == 'POST': pass
#设置cookie ret = HttpResponse('xxxxx') ret.set_cookie(k1,v1,max_age=55) ret.set_signed_cookie(k1,v1,max_age=55,salt='xxx') #获取cookie request.COOKIES.get('k1') request.get_signed_cookie(k1,salt='xxx',default='xxxx')
#设置session request.session['pk']=obj.pk #获取session pk = request.session.get('pk')
4、orm和Model和ModelForm,modelformset_factory
1、orm之models字段和查询方法、双下方法、外键、多对多、聚合分组、F和Q
#models类包含的数据类型,models.Model from django.db import models from multiselectfield import MultiSelectField from django.utils.safestring import mark_safe pid = models.AutoField(primary_key=True) delete_status = models.BooleanField(verbose_name='删除状态', default=False) contract_agreed = models.BooleanField("我已认真阅读完培训协议并同意全部协议内容", default=False) name=models.CharField(max_length=32, verbose_name='姓名') money = models.IntegerField(verbose_name='付费金额') price = models.DecimalField(max_digits=5, decimal_places=2) birthday = models.DateField('出生日期', default=None, help_text="格式yyyy-mm-dd", blank=True, null=True) last_consult_date = models.DateField("最后跟进日期", auto_now_add=True) date = models.DateTimeField() birth = models.DateTimeField(auto_now=True) homework = models.FileField(verbose_name='作业文件', blank=True, null=True, default=None) pub = models.ForeignKey('Publisher',on_delete=models.CASCADE) consultant = models.ForeignKey('UserProfile', verbose_name="销售", related_name='customers', blank=True, null=True, ) author = models.ForeignKey('Author', on_delete=models.CASCADE) roles=models.ManyToManyField('Role',blank=True) books = models.ManyToManyField('Book',through='Author_book') user=models.OneToOneField(User,on_delete=models.CASCADE)
#查询 必知必会13条 #返回对象列表all() 、filter() 、exclude()、values() [{}]、values_list() [()]、order_by() 、reverse()、distinct() #返回对象的get() 、first()、last() #返回布尔值extists() #返回数字的count() #单表的双下滑线__gt gte lt lte ;__range = [1,5];_in = [1,6] ;__contains 包含 like ;__icontains;__isnull q.children.append(Q(('name__contains','xxxx'))) # 外键 publisher = model.ForeignKey('Publisher',on_delete=models.CASCADE) #基于对象的查询 正向 多 —— 》 一 book_obj.publisher ——》 所关联的对象 book_obj.publisher_id ——》 数据库存的一个字段 反向 一 ——》 多 publisher_obj.book_set ——》 关系管理对象 publisher_obj.book_set.all() 指定related_name='books' publisher_obj.books——》 关系管理对象 publisher_obj.books.all() 基于字段的查询 models.Book.objects.filter(publisher__name='xxxx') models.Publisher.objects.filter(book__name='xxxx') # 多对多 class_list = models.ManyToManyField('ClassList', verbose_name="已报班级", blank=True, ) #关系管理对象 user.class_list ——》 关系管理对象 clsss_obj.customer_set user.class_list.all() user.class_list.set([]) [班级对象,班级对象] [id,id ] add() remove() clear() user.class_list.create(班级的信息) #F和Q F('kucun') Q(name='xxxx') filter(Q(name='xxxx')|Q(name='xxxx')) | 或 & 与 ~ 非 Q(('name__contains','xxxx')) #分组和聚合 ret = models.UserProfile.objects.aggregate(count =Count('id')) ret = models.ClassList.objects.annotate(Count('customer')).values() # annotate 注释 ret = models.Customer.objects.values('class_list').annotate(Count('id')) 事务: from django.db import transaction with transaction.atomic(): # 一系列ORM操作 models.UserProfile.objects.filter(id__gt=3).select_for_update()
2、Model和ModelForm,modelformset_factory
#models.py文件中 from django.db import models class Department(models.Model): """ 部门表 """ name = models.CharField(max_length=32, verbose_name="部门名称") count = models.IntegerField(verbose_name="人数", default=0)
#forms.py文件中 from django import forms from crm import models class BSForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field in self.fields.values(): if not isinstance(field, forms.BooleanField): field.widget.attrs.update({'class': 'form_control'}) class RegForm(BSForm): # password=forms.CharField(widget=forms.PasswordInput,min_length=6) re_pwd = forms.CharField(widget=forms.PasswordInput(), min_length=6, label='确认密码') class Meta: model = models.UserProfile fields = '__all__' # ['username','password'] #'__all__' exclude = ['memo', 'is_active'] labels = { 'username': '用户名' } widgets = { 'password': forms.PasswordInput(attrs={'class': 'form_control'}) } def clean(self): password = self.cleaned_data.get('password') re_pwd = self.cleaned_data.get('re_pwd') if password == re_pwd: return self.cleaned_data self.add_error('re_pwd', '两次密码不一致') raise ValidationError('两次密码不一致') class CustomerForm(BSForm): class Meta: model = models.Customer fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['course'].widget.attrs.pop('class') class ConsultForm(BSForm): class Meta: model = models.ConsultRecord fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) customer_choices=[(customer.pk,str(customer))for customer in self.instance.consultant.customers.all()] customer_choices.insert(0,('','---------')) self.fields['customer'].choices=customer_choices self.fields['consultant'].choices=[('','---------'),(self.instance.consultant.pk,str(self.instance.consultant))]
#modelformset_factory的使用 from django.forms import modelformset_factory def studyrecordlist(request,courserecord_id): FormSet=modelformset_factory(models.StudyRecord,StudyRecordForm,extra=0) all_studyrecord=models.StudyRecord.objects.filter(course_record_id=courserecord_id) form_obj=FormSet(queryset=all_studyrecord) if request.method=='POST': form_obj=FormSet(request.POST) if form_obj.is_valid(): form_obj.save() return render(request,'teacher/study_record_list.html',{'form_obj':form_obj})
formset前端使用
<form action="?type=update" method="post"> {% csrf_token %} {{ update_formset.management_form }} <table class="table table-bordered"> <thead> <tr> <th>序号</th> </tr> </thead> <tbody> {% for form in update_formset %} <tr> {{ form.id }} <td style="vertical-align: middle;">{{ forloop.counter }}</td> </tr> {% endfor %} </tbody> </table> </div> </form> </div> {% endblock %}
#models.py文件中 from django.db import models class User(models.Model): username = models.CharField(max_length=32, verbose_name='用户名') password = models.CharField(max_length=32,null=True,blank=True) gender = models.IntegerField(choices=((1, '男'), (2, '女'))) email = models.EmailField(null=True,blank=True) phone = models.CharField(max_length=11, null=True,blank=True)
forms.py文件中的RegForm与上面的定义相同显示效果
#forms.py文件中 from django import forms from app01 import models from django.core.exceptions import ValidationError from django.core.validators import RegexValidator def sxxx(value): #书写自定义校验规则,将函数加载自定义Field中,可实现自定义校验 raise ValidationError('xxx') class RegForm(forms.Form): username = forms.CharField(max_length=6, label='用户名', initial='初始值', error_messages={}) password = forms.CharField(min_length=6, widget=forms.PasswordInput(attrs={'class': 'form-control'})) gender = forms.ChoiceField(choices=((1, '男'), (2, '女'))) email = forms.EmailField() phone = forms.CharField(max_length=11, validators=[sxxx, RegexValidator(r'1[3-9]\d{9}', '手机号格式不正确')]) class RegForm2(forms.ModelForm): phone = forms.CharField(max_length=11,required=False, validators=[RegexValidator(r'1[3-9]\d{9}', '手机号格式不正确')]) re_password = forms.CharField(min_length=11,required=False) class Meta: model = models.User fields = ['username','password','re_password','gender','email','phone',] # exclude = [] labels = { 'username': '用户名' } widgets = { 'password': forms.PasswordInput() } error_messages = { 'email': { 'invalid': '邮箱格式不正确' } } def clean_gender(self): # 校验成功 返回当前字段的值 # 校验不成功 抛出异常 data = self.cleaned_data.get('gender') return data def clean(self): # 校验成功 返回所有的字段的值 # 校验不成功 抛出异常 # self.add_error('') return self.cleaned_data
#views.py文件中 from django.shortcuts import render from app01.forms import RegForm, RegForm2 from app01 import models # Create your views here. def form_test(request, pk=None): obj = models.User.objects.filter(pk=pk).first() from_obj = RegForm2(instance=obj) if request.method == 'POST': from_obj = RegForm2(request.POST, instance=obj) if from_obj.is_valid(): from_obj.save() return render(request, 'form.html', {'from_obj': from_obj}) from django.forms import modelformset_factory def modelformset_test(reqeust): ModelFormSet = modelformset_factory(models.User, RegForm2,extra=0) query_set = models.User.objects.all() formset = ModelFormSet(queryset=query_set) if reqeust.method =='POST': formset = ModelFormSet(reqeust.POST) if formset.is_valid(): formset.save() return render(reqeust,'form_set.html',{'formset':formset})
#form前端显示 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="" method="post" novalidate> {% csrf_token %} {% for field in from_obj %} <p> <label for="{{ field.id_for_label }}"> {{ field.label }}</label> {{ field }}{{ field.errors.0 }} </p> {% endfor %} {{ from_obj.non_field_errors }} <button>提交</button> </form> </body> </html>
#form_set前端显示 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="" method="post"> {% csrf_token %} {{ formset.management_form }} {% for form in formset %} <p> {{ form.id }} {{ form.username }} {{ form.gender }} {{ form.email }} {{ form.instance.password }} </p> {% endfor %} {{ formset.errors }} <button>提交</button> </form> </body> </html>
{% extends 'base.html' %} {% block css %} {% endblock %} <!--base.html--> {% block tools %} {% include 'tools.html' %} {% endblock %} #判断语句 {% if request.path_info == '/crm/customer_list/' %} <option value="multi_apply">公户变私户</option> {% else %} <option value="multi_pub">私户变公户</option> {% endif %} #循环语句 {% for customer in all_customer %} <td>{{ forloop.counter }}</td> <td>{{ customer.name|default:'未填写' }}</td> <td>{{ customer.get_sex_display }}</td> <td>{{ customer.show_status|safe }}</td> {% endfor %}
#定义函数
from django.template import Library
register=Library()
@register.simple_tag def reverse_url(request,name,*args,**kwargs): base_path=reverse(name,args=args,kwargs=kwargs) next=request.get_full_path() dict=QueryDict(mutable=True) dict['next']=next return '{}?{}'.format(base_path,dict.urlencode()) #调用 {% load my_tags %} <td><a href="{% reverse_url request 'edit_enrollment' enrollment.pk %}"><i class="fa fa-pencil-square-o"></i></a></td>
# filter @register.filter def x1(value,arg='xxx'): return True # simple_tag @register.simple_tag def x2(*args,**kwargs): return True @register.inclusion_tag('li.html') def show_li(num): return {'num':range(num)}
from django.views import View class CustomerList(View): def get(self,request): q=self.search(['qq','name']) # 公户展示 if request.path_info == reverse('customer_list'): all_customer = models.Customer.objects.filter(q,consultant__isnull=True) else: all_customer = models.Customer.objects.filter(q,consultant=request.user_obj) page = Pagination(request.GET.get('page'), all_customer.count(),request.GET.copy(), per_num=10) return render(request, 'consultant/customer_list.html', {'all_customer': all_customer[page.start:page.end], 'page_html':page.page_html}) def post(self,request): action=request.POST.get('action') if not hasattr(self,action): return HttpResponse('非法操作') else: ret=getattr(self,action)() if ret: return ret return self.get(request) def multi_apply(self): ids=self.request.POST.getlist('ids') #判断客户数量是否已达上限 if self.request.use_obj.customers.all().count()+len(ids)>MAX_CUSTOMER_NUM: return HttpResponse('用户数量已达上限,请释放部分用户后再选择') with transaction.atomic(): queryset=models.Customer.objects.filter(pk__in=ids,consultant__isnull=True).select_for_update() if len(ids)==queryset.count(): queryset.update(consultant=self.request.user_obj) return return HttpResponse('数据已改动,请重新选取!')
#settings中配置 from app01 import views url(r'^add_pub/',views.Addpub.as_view())
CBV加装饰器三种方式:
from django.utils.decorators import method_decorator #1. 直接加在方法上 @method_decorator(timer) def get(self, request): #2. 加在dispatch方法上 @method_decorator(timer) def dispatch(self, request, *args, **kwargs): ret = super().dispatch(request, *args, **kwargs) return ret #3. 加在类上 @method_decorator(timer, 'post') @method_decorator(timer, 'get') class AddPublisher(View):
from django.views import View class AddPublisher(View): http_method_names = ['get'] def dispatch(self,request,*args,**kwargs): # 操作 ret = super().dispatch(request,*args,**kwargs) # 操作 return ret def get(self,request,*args,**kwargs): self.request # 获取列表 获取一个对象 pass def post(self,request,*args,**kwargs): # 提交 新增 pass def put(self,request,*args,**kwargs): # 修改一个对象的数据 pass def delete(self,request,*args,**kwargs): # 删除数据 pass # 应用 url(r'add_publisher',AddPublisher.as_view())
from django.utils.decorators import method_decorator # 1.加在类上 @method_decorator(wrapper,name='get') @method_decorator(wrapper,name='post') class AddPublisher(View): # 2.加在方法上 @method_decorator(wrapper) # 装饰器中函数的第个参数 —— 》 request @wrapper # 装饰器中函数的第个参数 —— 》 self def post(self,request,*args,**kwargs): # 3.加在dispatch方法上 @method_decorator(wrapper) def dispatch(self,request,*args,**kwargs): @method_decorator(wrapper,name='dispatch') class AddPublisher(View):
# csrf相关的装饰器 from django.views.decorators.csrf import csrf_exempt,csrf_protect,ensure_csrf_cookie csrf_exempt 只能加在dispatch
next = request.GET.get('next') action=request.POST.get('action') form_obj = ClassForm(request.POST, instance=obj) from django.http.request import QueryDict all_consult = models.ConsultRecord.objects.filter(q,delete_status=False,consultant=request.user_obj).order_by('-date') return render(request, 'teacher/class_list.html',{'all_class': all_class[page.start:page.end], 'page_html': page.page_html}) {% if request.path_info == '/crm/customer_list/' %} <option value="multi_pub">私户变公户</option> {% endif %}
1. request .method ——》 请求方式 POST GET 2. request .GET ——》 url上携带的参数 3. request .POST ——》 POST请求提交的数据 4. request .body ——》 请求体 5. request .FILES ——》 上传的文件 1. enctype = 'mutlipart/form-data' 2. POST {% csrf_token %} 3. 使用文件对象 chunks() 6. request.path_info ——》 url路径 不包含IP和端口、参数 7. request.COOKIES 8. request.session 9. request.get_full_path() ——》 url路径 不包含IP和端口 包含参数 10. request.get_host() ——》 主机的ip和端口 11. request.is_ajax() ——》 是否是ajax请求
1. HttpResponse() 2. render(request,'HTML文件名',{ }) 3. redirect(‘要跳转的地址’) 1. ret = HttpResponse('') ret['Location '] = ' 地址' 4. JsonResponse(字典) JsonResponse([] ,safe=False ) Content-Type = 'application/json'
form表单指定编码方式enctype="multipart/form-data";
从request.FILES中 文件对象;
f1.chunks() # 大文件使用;
#前端代码 <form enctype="multipart/form-data"> <div class="file-loading"> <input id="kv-explorer" type="file" multiple> </div> <br> <div class="file-loading"> <input id="file-0a" class="file" type="file" multiple data-min-file-count="1"> </div> <br> <button type="submit" class="btn btn-primary">Submit</button> <button type="reset" class="btn btn-default">Reset</button> </form>
#后端获取 file_obj = request.FILES.get('x1') form = FileForm(request.POST, request.FILES) status = handle_uploaded_file(request.FILES.getlist('file_field'), form.cleaned_data["team"].name, t)