zoukankan      html  css  js  c++  java
  • Django实战总结

    一, 在模板和视图中反解Form组件中的choices.

    // 前端中获得FORM中choices选项的对应值
    obj.get_字段名称_display
    
    // PY代码中获得FORM中choices选项的对应值
    obj.get_字段名称_display()
    

    二, 分页

    方式一

    # 先来一个视图
    class CustomerList(View):
        def get(self, request):
        	# 获取所有的数据
            resp = models.Customer.objects.all()
            # 每页显示20条数据
            paginator = Paginator(resp, 20)
            page = request.GET.get('page', 1)
            all_customer = paginator.page(page)
            return render(request, 'customer_list.html', {'all_customer': all_customer})
    
    // 前端代码如下
    <nav aria-label="Page navigation" class="text-right">
        <ul class="pagination">
            {% if all_customer.has_previous %}
                <li>
                    <a href="?page=1" aria-label="Previous">
                        <span aria-hidden="true">&laquo;</span>
                    </a>
                </li>
                <li><a href="?page={{ all_customer.previous_page_number  }}">上一页</a></li>
            {% endif %}
    
            <li>
                <a>
                    第 {{ all_customer.number }}页 共 {{ all_customer.paginator.num_pages }}页
                </a>
            </li>
            {% if all_customer.has_next %}
                <li><a href="?page={{ all_customer.next_page_number }}">下一页</a></li>
                <li>
                    <a href="?page={{ all_customer.paginator.num_pages }}" aria-label="Next">
                        <span aria-hidden="true">&raquo;</span>
                    </a>
                </li>
            {% endif %}
    
        </ul>
    </nav>
    

    方式二

    # 视图函数
    # 测试分页
    class Pagination(View):
        def get(self, request):
            # 所有数据(模拟309条数据)
            all_data = [{'name': f'user{i}', 'pwd': '123'} for i in range(1, 310)]
            max_data_count = len(all_data)
            # 每页显示的数据条数
            every_page_count = 10
            # 最大分页数
            page_count_max, more = divmod(max_data_count, every_page_count)
            if more:
                page_count_max += 1
            # 请求的页数(当前页)
            page = request.GET.get('page')
            try:
                page = int(page)
                if page < 1:
                    page = 1
                elif page > page_count_max:
                    page = page_count_max
            except Exception:
                page = 1
            # 起始数据
            start_count = (page - 1) * every_page_count
            # 结束数据
            end_count = page * every_page_count
    
            # 每页最大展示页数按钮数
            page_show_count = 9
            page_show_count_half = page_show_count // 2
            # 如果每页展示的页数按钮大于等于总共的页数按钮
            if page_show_count >= page_count_max:
                start_page = 1
                end_page = page_count_max
            else:
                # 起始页
                start_page = page - page_show_count_half
                # 终止页
                end_page = page + page_show_count_half
                if start_page < 1:
                    start_page = 1
                    end_page = page_show_count
                elif end_page > page_count_max:
                    end_page = page_count_max
                    start_page = end_page - page_show_count + 1
    
            # 生成按钮
            button_list = []
            if page <= 1:
                button_list.append(
                    f'<li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>')
            else:
                button_list.append(
                    f'<li><a href="?page={page - 1}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>')
            for i in range(start_page, end_page + 1):
                if i == page:
                    button_list.append(f'<li class="active"><a href="?page={i}">{i}</a></li>')
                else:
                    button_list.append(f'<li><a href="?page={i}">{i}</a></li>')
    
            if page >= page_count_max:
                button_list.append(
                    f'<li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&raquo;</span></a></li>')
            else:
                button_list.append(
                    f'<li><a href="?page={page + 1}" aria-label="Previous"><span aria-hidden="true">&raquo;</span></a></li>')
            button_html = mark_safe(''.join(button_list))
            return render(request, 'pagination.html', {'all_data': all_data[start_count:end_count],
                                                       'button_html': button_html
                                                       })
    
    // 前端
        <table class="table table-bordered table-hover">
            <thead>
            <tr>
                <th>用户名</th>
                <th>密码</th>
            </tr>
            </thead>
            <tbody>
            {% for data in all_data %}
                <tr>
                    <td>{{ data.name }}</td>
                    <td>{{ data.pwd }}</td>
                </tr>
            {% endfor %}
            </tbody>
        </table>
        <nav aria-label="Page navigation" class="text-right">
            <ul class="pagination">
                {{ button_html }}
            </ul>
        </nav>
    

    方式三

    # 将方式二提取出一个类
    from django.utils.safestring import mark_safe
    
    class MyPagination:
        def __init__(self, max_data_count, page, every_page_count=10, page_show_count=9):
            # 所有数据条数
            max_data_count = max_data_count
            # 每页显示的数据条数
            every_page_count = every_page_count
            # 最大分页数
            self.page_count_max, more = divmod(max_data_count, every_page_count)
            if more:
                self.page_count_max += 1
            # 请求的页数(当前页)
            try:
                self.page = int(page)
                if self.page < 1:
                    self.page = 1
                elif self.page_count_max and self.page > self.page_count_max:
                    self.page = self.page_count_max
            except Exception:
                self.page = 1
            # 起始数据
            self.start_count = (self.page - 1) * every_page_count
            # 结束数据
            self.end_count = self.page * every_page_count
    
            # 每页最大展示页数按钮数
            page_show_count = page_show_count
            page_show_count_half = page_show_count // 2
            # 如果每页展示的页数按钮大于等于总共的页数按钮
            if page_show_count >= self.page_count_max:
                self.start_page = 1
                self.end_page = self.page_count_max
            else:
                # 起始页
                self.start_page = self.page - page_show_count_half
                # 终止页
                self.end_page = self.page + page_show_count_half
                if self.start_page < 1:
                    self.start_page = 1
                    self.end_page = page_show_count
                elif self.end_page > self.page_count_max:
                    self.end_page = self.page_count_max
                    self.start_page = self.end_page - page_show_count + 1
    
        @property
        def show_html(self):
            # 生成按钮
            button_list = []
            if self.page <= 1:
                button_list.append(
                    f'<li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>')
            else:
                button_list.append(
                    f'<li><a href="?page={self.page - 1}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>')
            for i in range(self.start_page, self.end_page + 1):
                if i == self.page:
                    button_list.append(f'<li class="active"><a href="?page={i}">{i}</a></li>')
                else:
                    button_list.append(f'<li><a href="?page={i}">{i}</a></li>')
    
            if self.page >= self.page_count_max:
                button_list.append(
                    f'<li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&raquo;</span></a></li>')
            else:
                button_list.append(
                    f'<li><a href="?page={self.page + 1}" aria-label="Previous"><span aria-hidden="true">&raquo;</span></a></li>')
            return mark_safe(''.join(button_list))
    
    
    # 视图函数中
    # 测试分页
    class Pagination(View):
        def get(self, request):
            # 所有数据(模拟309条数据)
            all_data = [{'name': f'user{i}', 'pwd': '123'} for i in range(1, 310)]
            max_data_count = len(all_data)
            page = MyPagination(max_data_count, request.GET.get('page', 1))
            return render(request, 'pagination.html', {'all_data': all_data[page.start_count:page.end_count],
                                                       'button_html': page.show_html
                                                       })
    

    进阶: 分页保留原有参数

    前提知识: QueryDict的方法

    # request.POST获得包含所有POST数据的QueryDict对象
    # request.GET获得包含所有Url中携带的参数的QueryDict对象
    # 假设参数有search(搜索条件)和page(页码)
    # {'search': 'xxx', 'page': 'xxx'}
    query_dict = request.GET # 默认不可修改
    query_dict._mutable = True # 设置成可修改
    query_dict['page'] = 1
    query_dict.copy() # 深拷贝一份QueryDict对象,并且改为可修改类型
    query_dict.urlencode() # ==> 字符串 search=xxx&page=xxx
    
    
    from django.utils.s9afestring import mark_safe
    from django.http.request import QueryDict
    
    class MyPagination:
        def __init__(self, max_data_count, page, query_dict=None, every_page_count=10, page_show_count=9):
            if not query_dict:
                # 实例化一个可编辑的QueryDict
                query_dict = QueryDict(mutable=True)
            self.query_dict = query_dict
            # 所有数据条数
            max_data_count = max_data_count
            # 每页显示的数据条数
            every_page_count = every_page_count
            # 最大分页数
            self.page_count_max, more = divmod(max_data_count, every_page_count)
            if more:
                self.page_count_max += 1
            # 请求的页数(当前页)
            try:
                self.page = int(page)
                if self.page < 1:
                    self.page = 1
                elif self.page_count_max and self.page > self.page_count_max:
                    self.page = self.page_count_max
            except Exception:
                self.page = 1
            # 起始数据
            self.start_count = (self.page - 1) * every_page_count
            # 结束数据
            self.end_count = self.page * every_page_count
            # 每页最大展示页数按钮数
            page_show_count = page_show_count
            page_show_count_half = page_show_count // 2
            # 如果每页展示的页数按钮大于等于总共的页数按钮
            if page_show_count >= self.page_count_max:
                self.start_page = 1
                self.end_page = self.page_count_max
            else:
                # 起始页
                self.start_page = self.page - page_show_count_half
                # 终止页
                self.end_page = self.page + page_show_count_half
                if self.start_page < 1:
                    self.start_page = 1
                    self.end_page = page_show_count
                elif self.end_page > self.page_count_max:
                    self.end_page = self.page_count_max
                    self.start_page = self.end_page - page_show_count + 1
    
        @property
        def show_html(self):
            # 生成按钮
            button_list = []
            # 获取完整的参数显示
            parameters = self.query_dict
            if self.page <= 1:
                button_list.append(
                    f'<li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>')
            else:
                parameters['page'] = self.page - 1
                button_list.append(
                    f'<li><a href="?{parameters.urlencode()}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>')
            for i in range(self.start_page, self.end_page + 1):
                parameters['page'] = i
                if i == self.page:
                    button_list.append(f'<li class="active"><a href="?{parameters.urlencode()}">{i}</a></li>')
                else:
                    button_list.append(f'<li><a href="?{parameters.urlencode()}">{i}</a></li>')
    
            if self.page >= self.page_count_max:
                button_list.append(
                    f'<li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&raquo;</span></a></li>')
            else:
                parameters['page'] = self.page + 1
                button_list.append(
                    f'<li><a href="?{parameters.urlencode()}" aria-label="Previous"><span aria-hidden="true">&raquo;</span></a></li>')
            return mark_safe(''.join(button_list))
    
    

    三, 在模板中使用Model From

    <form action="" method="post" class="form-horizontal col-md-8 col-md-offset-1" novalidate>
        {% csrf_token %}
        {% for field in form_obj %}
            <div class="form-group {% if field.errors %}has-error{% endif %}">
                <label for="{{ field.id_for_label }}"
                       class="col-md-4 control-label {% if not field.field.required %}not_required{% endif %}">
                    {{ field.label }}</label>
    
                <div class="col-md-8">
                    {{ field }}
                    <span class="help-block">{{ field.errors.0 }}</span>
                </div>
            </div>
        {% endfor %}
    
        <p class="text-right">
            <button type="reset" class="btn btn-default">重置</button>
            <button type="submit" class="btn btn-primary">提交</button>
        </p>
    </form>
    

    在模板中可以直接使用 request 对象

    四, 使用Model Form进行编辑

    # 编辑客户
    class CustomerEdit(View):
        def get(self, request, pk):
            customer = models.Customer.objects.filter(pk=pk).first()
            if not customer:
                return HttpResponse('要编辑的数据不存在')
            form_obj = CustomerForm(instance=customer)
            return render(request, 'customer_edit.html', {'form_obj': form_obj})
    
        def post(self, request, pk):
            customer = models.Customer.objects.filter(pk=pk).first()
            form_obj = CustomerForm(data=request.POST, instance=customer)
            if form_obj.is_valid():
                form_obj.save()  # 编辑
                return redirect('crm:customer_list')
            return render(request, 'customer_edit.html', {'form_obj': form_obj})
    

    进阶:

    Model Form的保存流程:

    instance: 如果提供instance则使用传入的instance,如果未提供,根据meta中的model生成对应的model对象.

    form_obj.save(): 遍历更新instance的字段(fileds中指定的字段),然后调用instance对象的save()方法

    所以,可以在is_valid()方法后,通过form_obj.instance新增字段信息(exclude中排除的字段),然后调用save()

    也可以在实例化Model Form时给instance传入新创建的model对象(可以添加想要的字段),依据instance,在init方法中修改字段的相关选项等待.

    五, 验证登录状态

    class MyMiddleware(MiddlewareMixin):
        
        # 验证登录状态
        def process_request(self, request):
            url = request.path_info
            # 白名单
            if url in [reverse('crm:login'), reverse('crm:register')]:
                return None
            # admin也要免验证
            if url.startswith('/admin'):
                return None
            
            is_login = request.session.get('is_login')
            if not is_login:
                return redirect(f"{reverse('crm:login')}?next={url}")
            user_id = request.session.get('user_id')
            request.user_obj = models.UserProfile.objects.filter(pk=user_id).first()
    

    六, CBV之反射

    # 展示客户(公户/私户)/模糊查询/批量操作
    class CustomerList(View):
        def get(self, request):
            url = request.path_info
            q = self.search_q(['qq', 'qq_name', 'name', 'phone'])
            if url == reverse('crm:customer_list'):
                all_customer = models.Customer.objects.filter(q, consultant_id=None)
            else:
                all_customer = models.Customer.objects.filter(q, consultant_id=request.user_obj)
            page = MyPagination(all_customer.count(), request.GET.get('page', 1), request.GET.copy())
            return render(request, 'customer_list.html', {
                'all_customer': all_customer[page.start_count:page.end_count],
                'button_html': page.show_html,
            })
    
        def post(self, request):
            action = request.POST.get('action')
            # 通过反射获取对应的方法
            if not hasattr(self, action):
                return HttpResponse('请求不合法')
            getattr(self, action)()
            # 仍然返回当前页面
            return self.get(request)
    
        # 转换为公户
        def to_public(self):
            pk_list = self.request.POST.getlist('customer_pk')
            customers = models.Customer.objects.filter(pk__in=pk_list)
            # 方式一
            customers.update(consultant=None)
            # 方式二
            # self.request.user_obj.customers.remove(*customers)
    
        # 转换为私户
        def to_private(self):
            pk_list = self.request.POST.getlist('customer_pk')
            customers = models.Customer.objects.filter(pk__in=pk_list)
            # 方式一
            customers.update(consultant=self.request.user_obj)
            # 方式二
            # self.request.user_obj.customers.add(*customers)
    
        # 生成查询语句
        def search_q(self, field_list):
            search = self.request.GET.get('search', '')
            q = Q()
            q.connector = 'OR'
            for field in field_list:
                q.children.append((f'{field}__contains', search))
            return q
    
    

    七, Q查询的用法

    q = self.search_q(['qq', 'qq_name', 'name', 'phone'])
    
    # 生成查询语句
    def search_q(self, field_list):
        search = self.request.GET.get('search', '')
        q = Q()
        q.connector = 'OR'
        for field in field_list:
        	q.children.append((f'{field}__contains', search))
        return q
    

    八, 修改/新增后仍然返回当前页面

    思路: 将本页面的完整url传入修改/新增页面,在完成修改/新增后重定向到此url

    难点: url中有多个参数时,&等特殊字符会影响参数的获取

    解决: QueryDict的urlencode()方法

    创建一个自定义方法:

    from django import template
    from django.urls import reverse
    from django.http.request import QueryDict
    
    register = template.Library()
    
    @register.simple_tag
    def url_tag(request, url_name, *args, **kwargs):
        ulr = reverse(url_name, args=args, kwargs=kwargs)
        next_url = request.get_full_path()
        query_dict = QueryDict(mutable=True)
        query_dict['next'] = next_url
        return f'{ulr}?{query_dict.urlencode()}'
    

    模板中使用:

    {% load mytags %}
    <a href="{% url_tag request 'crm:customer_add' %}">新增</a>
    <a href="{% url_tag request 'crm:customer_edit' customer.pk %}">编辑</a>
    

    九, MySql行级锁

    # mysql中
    begin;
    select * from app01_user where id=1 for update;  # 加行级锁
    commit;
    
    # django中
    from django.db import transaction
    from django.conf import settings
    
    def to_private(self):
    	pk_list = self.request.POST.getlist('customer_pk')
    	if len(pk_list) + models.Customer.objects.filter(
            # 在settings.py中设置MAX_CUSTOMER_NUM客户上限
    		consultant=self.request.user_obj).count() > settings.MAX_CUSTOMER_NUM客户上线:
    		return HttpResponse('太多了')
    	try:
    		with transaction.atomic():
    			customers = models.Customer.objects.filter(pk__in=pk_list, consultant=None).select_for_update()
            	if len(pk_list) == customers.count():
            		customers.update(consultant=self.request.user_obj)
                else:
                    return HttpResponse('手速太慢了')
        except Exception:
        	pass
    

    十, 批量操作数据

    # 批量插入
    def make_study(self):
        course_pk_list = self.request.POST.getlist('course_pk')
        for course_pk in course_pk_list:
            course_obj = models.CourseRecord.objects.filter(pk=course_pk).first()
            class_obj = course_obj.re_class
            study_record_list = []
            for student in class_obj.customer_set.filter(status='studying'):
            	if not models.StudyRecord.objects.filter(course_record=course_obj, student=student).exists():
            		study_record_list.append(models.StudyRecord(course_record=course_obj, student=student))
    
            models.StudyRecord.objects.bulk_create(study_record_list)  # 批量插入
    

    modelformset:

    from django.forms import modelformset_factory
    # 展示学习记录
    class StudyList(MyView):
        def get(self, request, course_id=None, *args, **kwargs):
            # 工厂模型,针对model(StudyRecord)的每条数据,生成一个instance传入form中,最终生成一个form表单
            form_set = modelformset_factory(model=models.StudyRecord, form=StudyForm, extra=0)
            queryset = models.StudyRecord.objects.filter(course_record_id=course_id).order_by('pk')
            page = MyPagination(queryset.count(), request.GET.get('page', 1), request.GET.copy())
            formset_obj = form_set(queryset=queryset[page.start_count:page.end_count])
            return render(request, 'teacher/study_record.html', {
                'formset_obj': formset_obj,
                'button_html': page.show_html
            })
    
        def post(self, request, course_id=None, *args, **kwargs):
            form_set = modelformset_factory(model=models.StudyRecord, form=StudyForm, extra=0)
            queryset = models.StudyRecord.objects.filter(course_record_id=course_id).order_by('pk')
            page = MyPagination(queryset.count(), request.GET.get('page', 1), request.GET.copy())
            formset_obj = form_set(data=request.POST)
            # 依次验证每个form表单的数据,并更新,在from中可以用instance拿到对应的数据
            if formset_obj.is_valid():
                formset_obj.save()
                return self.get(request, course_id, *args, **kwargs)
            return render(request, 'teacher/study_record.html', {
                'formset_obj': formset_obj,
                'button_html': page.show_html
            })
    
    # 模板中
    <div class="table-responsive">
        <form action="" method="post">
            {% csrf_token %}
            {{ formset_obj.management_form }}
            <button class="btn btn-primary"><span><i
                    class="fa fa-address-book"></i></span>
                保存
            </button>
            <table class="table table-bordered table-condensed text-center" style="font-size: 12px;">
                <thead>
                <tr>
                    <th>序号</th>
                    <th>学生姓名</th>
                    <th>考勤</th>
                    <th>成绩</th>
                    <th>批语</th>
                </tr>
                </thead>
                <tbody>
                {% for form in formset_obj %}
                    <tr>
                        {{ form.id }}
                        <td>{{ forloop.counter }}</td>
                        <td>{{ form.instance.student }}</td>
                        <td>{{ form.attendance }}</td>
                        <td>{{ form.score }}</td>
                        <td>{{ form.homework_note }}</td>
                    </tr>
                {% endfor %}
                </tbody>
            </table>
        </form>
    </div>
    
    <nav aria-label="Page navigation" class="text-right">
        <ul class="pagination">
            {{ button_html }}
        </ul>
    </nav>
    
  • 相关阅读:
    UIProgressView的详细使用
    Android拍照上传代码样例
    UILabel的详细使用及特殊效果
    TextView属性android:ellipsize实现跑马灯效果
    Android中WebView实现Javascript调用Java类方法
    有效获取状态栏(StatusBar)高度
    详解iPhone Tableview分批显示数据
    TextView显示插入的图片
    ObjectiveC语法快速参考
    UISegmentedControl的详细使用
  • 原文地址:https://www.cnblogs.com/zyyhxbs/p/11678275.html
Copyright © 2011-2022 走看看