django 练习,分页,form 表单验证

django 写的一个在网页上对学生信息进行操作,用到了ajax,jquery操作表格等,还有 bootstrap,和 font-awesome(图标),这些框架写好了各种各样的网页元素,用起来十分的方便,
下面是效果图,


django 练习,分页,form 表单验证_第1张图片
#models.py 文件 的建表信息
from django.db import models

# Create your models here.

class Classes(models.Model):
    title = models.CharField(max_length=32)
    m = models.ManyToManyField("Teacher")

class Teacher(models.Model):
    name = models.CharField(max_length=32)

class Student(models.Model):
    username = models.CharField(max_length=32)
    age = models.IntegerField()
    gender = models.NullBooleanField()
    cs = models.ForeignKey("Classes",on_delete=models.CASCADE)

路由分发文件,

# urls.py 路由文件
from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path("students.html/", views.students),
    path("add_student/", views.add_student),
    path("del_students/", views.del_students),
    path("edit_student/", views.edit_student),

]

这个是视图函数文件,处理URL分发的事件

# views.py 文件
from django.shortcuts import render
from django.shortcuts import HttpResponse
from app01 import models
import json
# Create your views here.

def students(request):
    all_students = models.Student.objects.all()
    all_classes = models.Classes.objects.all()
    return render(request, "students.html",{
        "all_students": all_students,
        "all_classes": all_classes,
    })

def add_student(request):
    response = {'status':True,"message":None,"nid":None}
    try:
        username = request.POST.get("username")
        age = request.POST.get("age")
        gender = request.POST.get("gender")
        cls_id = request.POST.get("cls_id")
        print(username,age,gender,cls_id)
        obj = models.Student.objects.create(
            username=username,
            age=age,
            gender=gender,
            cs_id=cls_id,
        )
        response["nid"] = obj.id
    except Exception as e:
        response["status"] = False
        response["message"] = "用户输入错误"
    import json
    response_json = json.dumps(response)
    return HttpResponse(response_json)

def del_students(request):
    if request.method == "POST":
        response = {"status":True}
        try:
            nid = request.POST.get("nid")
            models.Student.objects.filter(id=nid).delete()
        except Exception as e:
            response["status"] = False
        response_ret = json.dumps(response)
        return HttpResponse(response_ret)

def edit_student(request):
    response = {'code':1000, 'message': None}
    if request.method == "POST":
        try:
            nid = request.POST.get("nid")
            username = request.POST.get("username")
            age = request.POST.get("age")
            gender = request.POST.get("gender")
            cs_id = request.POST.get("cls_id")
            print(nid,username,age,gender,cs_id)
            models.Student.objects.filter(id=nid).update(
                username=username,
                age=age,
                gender=gender,
                cs_id=cs_id,
            )
        except Exception as e:
            response["code"] = 1001
            response["message"] = str(e)

        return HttpResponse(json.dumps(response))

下面这个是主文件了,就只有一个html文件,主要通信都是用ajax

# 这个是 student.html 文件



    
    Title
    
    
    


添加

    {% for foo in all_students %}
        
            {% if foo.gender %}
                
            {% else %}
                
            {% endif %}

            
    {% endfor %}
    
ID 姓名 年龄 性别 班级 操作
{{ foo.id }} {{ foo.username }} {{ foo.age }}{{ foo.cs.title }}



添加按钮提示框,当用户输入有问题时就会提示 ’ 用户输入错误 ‘ ,

django 练习,分页,form 表单验证_第2张图片



下面这个是编辑框,点击打开某个学生时会默认选中,
django 练习,分页,form 表单验证_第3张图片



整个总体来说写的还是存在一个bug,例如在添加学生信息时,页面上是用 js 创建tr表格的,各种属性没有添加上去,
页面信息有时候用js添加比较好,不用刷新整个页面,而有时候是页面整个刷新为好,看各种情况了。
下面是总结呵,

1. 
    Python序列化
        字符串 = json.dumps(对象)    对象->字符串
        对象 = json.loads(字符串)    字符串->对象
        
    JavaScript:
        字符串 = JSON.stringify(对象) 对象->字符串
        对象 = JSON.parse(字符串)     字符串->对象
        
    应用场景:
        数据传输时,
            发送:字符串
            接收:字符串 -> 对象
2. ajax

    $.ajax({
        url: 'http//www.baidu.com',
        type: 'GET',
        data: {'k1':'v1'},
        success:function(arg){
            // arg是字符串类型
            // var obj = JSON.parse(arg)
        }
    })
    
    
    $.ajax({
        url: 'http//www.baidu.com',
        type: 'GET',
        data: {'k1':'v1'},
        dataType: 'JSON',
        success:function(arg){
            // arg是对象
        }
    })
    
    
    $.ajax({
        url: 'http//www.baidu.com',
        type: 'GET',
        data: {'k1':[1,2,3,4]},
        dataType: 'JSON',
        success:function(arg){
            // arg是对象
        }
    })
    
    发送数据时:
        data中的v
        
        a. 只是字符串或数字
            $.ajax({
                url: 'http//www.baidu.com',
                type: 'GET',
                data: {'k1':'v1'},
                dataType: 'JSON',
                success:function(arg){
                    // arg是对象
                }
            })
        b. 包含属组
            $.ajax({
                url: 'http//www.baidu.com',
                type: 'GET',
                data: {'k1':[1,2,3,4]},
                dataType: 'JSON',
                traditional: true, //// 要加这一句
                success:function(arg){
                    // arg是对象
                }
            })
            
        c. 传字典,或者将其转换成字符串
        
            b. 包含属组
            $.ajax({
                url: 'http//www.baidu.com',
                type: 'GET',
                data: {'k1': JSON.stringify({}) },
                dataType: 'JSON',
                success:function(arg){
                    // arg是对象
                }
            })
            
    
    
3. 事件委托

    $('要绑定标签的上级标签').on('click','要绑定的标签',function(){})

    $('要绑定标签的上级标签').delegate('要绑定的标签','click',function(){})

4. 编辑
    

5. 总结

        新URL方式: //也就是跳转到新页面的方式
            - 独立的页面
            - 数据量大或条目多
            
        对话框方式:
            - 数据量小或条目少
                -增加,编辑
                    - Ajax: 考虑,当前页;td中自定义属性
                    - 页面(***)
                
        删除:
            对话框

这个就到这里了。路漫漫其修远兮

django 分页

补充:
写一个对页面分页的类,django的内置分页太丑了

# tests.py 文件,建立自定制的类
class couPaginator(object):
    def __init__(self,totalCount,CurrentPage,perPageItemNum=30,maxPageNum=8):
        # 数据总个数
        self.total_count = totalCount
        # 当前页
        self.current_page = CurrentPage
        # 每页最多显示的条数
        self.per_page_item_num = perPageItemNum
        # 最多显示页面
        self.max_page_num = maxPageNum

        self.check()

    def check(self): # 检查数据,
        try:
            self.total_count = int(self.total_count)
            self.current_page = int(self.current_page)
            if self.current_page > self.num_pages: # 如果当前页大于最大页数
                self.current_page=self.num_pages
            if self.current_page <= 1: # 如果当前页小于等于1
                self.current_page=1
        except Exception as e:
            pass
    @property # 装饰器,可以使得函数不加 () 直接调用
    def num_pages(self):
        a,b=divmod(self.total_count,self.per_page_item_num) # 总数目/每页显示数据,返回整除和余数
        if b==0:
            return a
        else:
            return a+1 
    def start(self): # 返回 开始
        return (self.current_page-1)*self.per_page_item_num
    def end(self): # 返回结束
        return self.current_page*self.per_page_item_num

    @property
    def pager_num_range(self): # 这个是分页函数,也就是页面下面的一排跳转页面
        if self.num_pages <= self.max_page_num:
            return range(1,self.num_pages+1)
        if self.current_page <= self.max_page_num/2:
            return range(1,self.max_page_num+1)
        part = int(self.max_page_num/2)
        if self.current_page+part>=self.num_pages+1:
            return range(self.num_pages-self.max_page_num+1,self.num_pages+1)
        return range(self.current_page-part,self.current_page+part)
    
    def page_str(self): # 对上面的分页函数转成html,可以直接在模版渲染出来,不用再做另外的转换
        test_list=[]
        if self.current_page==1:
            prev = "
  • 上一页
  • " else: prev = "
  • 上一页
  • " %(self.current_page-1) if self.current_page==self.max_page_num: next = "
  • 下一页
  • " else: next = "
  • 下一页
  • " %(self.current_page+1) test_list.append(prev) test_list.append(next) for i in self.pager_num_range: if i == self.current_page: temp = "
  • %s
  • " %(i,i) else: temp = "
  • %s
  • " %(i,i) test_list.append(temp) first = "
  • 首页
  • " last = "
  • 尾页
  • " %(self.num_pages) test_list.append(first) test_list.append(last) return ' '.join(test_list)

    模版文件

    # index2.html 文件
    
    
    
        
        Title
        
    
    
        {% for foo in data %}
            

    {{ foo.name }} - {{ foo.age }}

    {% endfor %}
      {{ test.page_str|safe }}

    视图函数

    # 视图函数,views.py 文件
    test_list = []  # 创建数据
    for i in range(999):
        temp = {'name':'root'+str(i),'age':i}
        test_list.append(temp)
    
    def index2(request):
        p = request.GET.get("p")
        from app01.tests import couPaginator
        test = couPaginator(999,p,perPageItemNum=10)
        return render(request,"index2.html",{
            "data":test_list[test.start():test.end()],
            "test":test,
        })
    
    

    不要忘记在 urls.py 文件中添加路由导航,还有要在静态文件下添加bootstrap 文件,静态文件目录也需要配置

    2.分页组件
        - Django内置分页
            - Paginator、 Page   
            - 页面:include
        
        - 扩展Django的内置分页
            - CustomPaginator(Paginator)
        传入:
            - 所有数据 
            - 当前页
            - 每页显示30条
            - 最多页面7个
        
        - 自定义分页
        传入:
            - 所有数据的个数
            - 当前页
            - 每页显示30条
            - 最多页面7个
    
    


    form 表单验证,基本

    调用 django 内置的 Form 组件,对前端的输入进行验证和返回错误提示信息,

    # views.py 文件下的
    from django import forms
    from django.forms import fields
    class F1form(forms.Form):
        user = fields.CharField(
            max_length=18, # 最长
            min_length=6, # 最短
            required=True, # 限制不能为空
            error_messages={  # 自定制返回到前端的错误提示信息
                "required": "用户名不能为空",
                "max_length": "用户名太长",
                "min_length": "用户名太短",
            }
        )
        pwd = fields.CharField(
            min_length=32,
            required=True,
            error_messages={
                "min_length": "最短32个字符",
                "required": "密码不能为空",
            }
        )
        age = fields.IntegerField(
            required=True,
            error_messages={
                "required": "年龄不能为空",
                "invalid": "必须为数字",
            }
        )
        email = fields.EmailField(
            required=True,
            error_messages={
                "required": "邮箱不能为空",
                "invalid": "邮箱格式错误",
            }
        )
    
    
    def fm(request):
        if request.method == "GET":
            return render(request,"fm.html")
        if request.method == "POST":
            # user = request.POST.get("user")
            # age = request.POST.get("age")
            # pwd = request.POST.get("pwd")
            # email = request.POST.get("email")
            # print(user,age,pwd,email)
            obj = F1form(request.POST)
            if obj.is_valid():
                print(obj.cleaned_data) # 验证通过获取到用户输入的数据
            else:
                print(obj.errors) # 验证失败返回错误信息
            return render(request,"fm.html",{"obj":obj})
    
    

    前端的html文件

    # fm.html 文件,用obj.errors.user.0 来取到user错误信息的第一个,因为一个输入框会不止一个错误提示
    
    
    
        
        Title
        
    
    
    

    {{ obj.errors.user.0 }}

    {{ obj.errors.pwd.0 }}

    {{ obj.errors.age.0 }}

    {{ obj.errors.email.0 }}

    在URL路由文件urls.py 文件下要添加路径导航,
    path('fm.html/', views.fm),
    要注释掉 form 的锁 csrf

    django内置的 forms 还能自己生成html代码,也就是输入框,所以, views.py 和 fm.html 文件也可以改成下面的,

    # views.py 文件
    def fm(request):
        if request.method == "GET":
            obj = F1form()  # 改动的地方
            return render(request,"fm.html",{"obj":obj}) # 还有这里
        if request.method == "POST":
            # user = request.POST.get("user")
            # age = request.POST.get("age")
            # pwd = request.POST.get("pwd")
            # email = request.POST.get("email")
            # print(user,age,pwd,email)
            obj = F1form(request.POST)
            if obj.is_valid():
                print(obj.cleaned_data)
            else:
                print(obj.errors)
            return render(request,"fm.html",{"obj":obj})
    
    # html 文件中的 input 输入框就可以用 django 中的来代替了
    
    
    
        
        Title
        
    
    
    

    {{ obj.user }}{{ obj.errors.user.0 }}

    {{ obj.pwd }}{{ obj.errors.pwd.0 }}

    {{ obj.age }}{{ obj.errors.age.0 }}

    {{ obj.email }}{{ obj.errors.email.0 }}

    form 也可以验证ajax提交的数据,因为ajax提交的数据到服务器也是用post提取的,(具体看你ajax中type的设置了)

    
    Form组件
    
        - 对用户请求的验证
            - AJax
            - Form
        - 生成HTML代码
    
        a. 创建一个类
        b. 类中创建字段(包含正则表达式)
        c. GET
            obj = Fr()
            obj.user = > 自动生成HTML
            
        d. POST
            obj = Fr(request.POST)
            if obj.is_valid():
                obj.cleaned_data
            else:
                obj.errors
                return .... obj
        
    

    form 提交数据,验证数据的有效性,

    我们可以自定制正则,也可以用django内置的规则,

    # models.py 创建一个表格
    from django.db import models
    
    # Create your models here.
    
    class userInfo(models.Model):
        username = models.CharField(max_length=32)
        email = models.EmailField(max_length=32)
    
    
    # urls.py 
    from django.contrib import admin
    from django.urls import path
    from app01 import views
    from django.conf.urls import url
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('users/', views.users),
        path('add_user/', views.add_user),
        url("^edit_user-(\d)$", views.edit_user), #这个是之前版本的路由导航
    ]
    
    
    # fm.py 自创建的文件,放置 form 类
    from django import forms
    from django.forms import fields
    
    class UserForm(forms.Form):
        username = fields.CharField(
            max_length=32,
            required=True,
            error_messages={  # 自定制错误提示信息
                "required":"请输入用户名",
                "max_length": "必须输入32个字符",
            }
        )
        email = fields.EmailField(
            required=True,
            error_messages={
                "required": "必须输入邮箱",
                "invalid": "不是正确的邮箱格式",
            }
        )
    
    # views.py 
    from django.shortcuts import render
    from django.shortcuts import redirect
    from app01 import models
    
    from app01 import fm
    
    def users(request): # 返回所有的数据,也就是所有表格的信息
        data_list = models.userInfo.objects.all()
        return render(request,"users.html",{"data_list":data_list})
    
    def add_user(request): # 添加数据,跳转到add_user.html, 
        if request.method == "GET":
            obj = fm.UserForm()
            return render(request,"add_user.html",{"obj":obj})
        if request.method == "POST": # post 接收数据,用form检测格式
            obj = fm.UserForm(request.POST)
            if obj.is_valid():
                print(obj.cleaned_data)
                models.userInfo.objects.create(**obj.cleaned_data) # 检测通过创建数据
                return redirect("/users.html")
            else:
                return render(request,"add_user.html",{"obj":obj}) # 错误提示,返回错误信息
    
    def edit_user(request,nid): # 编辑信息,使用form为我们写好的生成html,在edit_user.html中
        if request.method == "GET":
            data = models.userInfo.objects.filter(id=nid)[0] # nid 在ulrs.py 文件正则取得
            obj = fm.UserForm({"username":data.username,"email":data.email})
            return render(request,"edit_user.html",{"obj": obj,"nid":nid})
        if request.method == "POST":
            obj = fm.UserForm(request.POST)
            if obj.is_valid():
                print(obj.cleaned_data)
                models.userInfo.objects.filter(id=nid).update(**obj.cleaned_data)
                return redirect("/users")
            else:
                print(obj.errors) # 记得,返回错误信息时还要返回一次nid
                return render(request,"edit_user.html",{"obj":obj,"nid":nid})
    
    
    # users.html  ----》user 函数(对应)
    
    
    
        
        Title
    
    
    添加
    {% for foo in data_list %}
        

    {{ foo.id }} {{ foo.username }} {{ foo.email }} 编辑

    {% endfor %}
    # add_user.html ----》add_user 函数
    
    
    
        
        Title
    
    
    

    {{ obj.username }}{{ obj.errors.username.0 }}

    {{ obj.email }}{{ obj.errors.email.0 }}

    # edit_user.html ----》edit_user
    
    
    
        
        Title
    
    
    

    {{ obj.username }}{{ obj.errors.username.0 }}

    {{ obj.email }}{{ obj.errors.email.0 }}

    django 练习,分页,form 表单验证_第4张图片


    Form组件
        - form表单(验证;保留上次内容)
            - 
        - Ajax(验证;无需上次内容)
            - 返回HttpResponse
            - 前端:跳转或错误信息
            
    
    所谓的扩展
            
    1. Form组件扩展:
    
        1.简单扩展
            利用Form组件自带的正则扩展:
                a. 方式一
                    from django.forms import Form
                    from django.forms import widgets
                    from django.forms import fields
                    from django.core.validators import RegexValidator
                     
                    class MyForm(Form):
                        user = fields.CharField(
                            error_messages={'invalid': '...'},
                            validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
                        )
                b. 方式二
                    from django.forms import Form
                    from django.forms import widgets
                    from django.forms import fields
                    from django.core.validators import RegexValidator
                     
                    class MyForm(Form):
                        user = fields.RegexField(r'^[0-9]+$',error_messages={'invalid': '...'})
                        
        2.基于源码流程
            a. 单字段
                from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
                class AjaxForm(forms.Form):
                    username = fields.CharField()
                    user_id = fields.IntegerField(
                        widget=widgets.Select(choices=[(0,'alex'),(1,'刘皓宸'),(2,'杨建'),])
                    )
                # 自定义方法 clean_字段名
                # 必须返回值self.cleaned_data['username']
                # 如果出错:raise ValidationError('用户名已存在')
                def clean_username(self):
                    v = self.cleaned_data['username']
                    if models.UserInfo.objects.filter(username=v).count():
                        # 整体错了
                        # 自己详细错误信息
                        raise ValidationError('用户名已存在')
                    return v
                def clean_user_id(self):
                    return self.cleaned_data['user_id']
                        
                     
            b. 整体错误验证
                class AjaxForm(forms.Form):
                    username = fields.CharField()
                    user_id = fields.IntegerField(
                        widget=widgets.Select(choices=[(0,'alex'),(1,'刘皓宸'),(2,'杨建'),])
                    )
                    # 自定义方法 clean_字段名
                    # 必须返回值self.cleaned_data['username']
                    # 如果出错:raise ValidationError('用户名已存在')
                    def clean_username(self):
                        v = self.cleaned_data['username']
                        if models.UserInfo.objects.filter(username=v).count():
                            # 整体错了
                            # 自己详细错误信息
                            raise ValidationError('用户名已存在')
                        return v
                    def clean_user_id(self):
                        return self.cleaned_data['user_id']
    
                    def clean(self):
                        value_dict = self.cleaned_data
                        v1 = value_dict.get('username')
                        v2 = value_dict.get('user_id')
                        if v1 == 'root' and v2==1:
                            raise ValidationError('整体错误信息')
                        return self.cleaned_data
                        
                        
            PS: _post_clean
    form结束了,就这些东西,扩展也并不是很多内容
                    
             
    2. Django序列化
        a.对象
        b.字典
        c.元祖
    
    ===================================
    
    
    首先能完成任务,
    
    亮点,分为两种,一参加过某种大型的架构,二某种东西研究得特别深,那就研究,
    
    1.笔试:
    
    2.面试
    
    方向:
        - 基础
        - 亮点
    
    基础:
        1.设计程序
        2.开发
    
    亮点:
        某框架的源码:
        Tornado - 异步非堵塞(IO多路复用)
    
    

    关于序列化问题,也就是 Queryset 类型转成 json 类型,可以参考
    博客:http://www.cnblogs.com/wupeiqi/articles/5246483.html
    http://www.cnblogs.com/wupeiqi/articles/6144178.html

    今天在编辑后丢了很多东西,好气啊

    关于 NON_FIELD_ERRORS,也就是 all 了,下面是一个补充:

    from django.shortcuts import render
    from django.shortcuts import HttpResponse
    from app01 import models
    
    from django import forms
    from django.forms import fields
    from django.forms import widgets
    from django.core.exceptions import ValidationError
    
    
    class LoginForm(forms.Form):
        username = fields.CharField(
            max_length=32,
            required=True,
            error_messages={
                'required':'请输入用户名',
                'max_length':'最长输入32个字符',
            },
            widget=widgets.TextInput()
        )
        # def clean_username(self):
        #     v1 = self.cleaned_data['username']
        #     v2 = models.User.objects.filter(username=v1).first()
        #     if not v2:
        #         raise ValidationError("用户名错误") # 这个引发的错误会在errors.username显示
        #     return v1
    
        password = fields.CharField(
            max_length=32,
            required=True,
            error_messages={
                'required':'请输入密码',
                'max_length':'最长输入32个字符'
            },
            widget=widgets.PasswordInput()
        )
    
        def clean(self):
            v1 = self.cleaned_data.get('username')
            v2 = self.cleaned_data.get('password')
            obj = models.User.objects.filter(username=v1).first()
            if not obj:
                raise ValidationError("用户名错误")
            elif obj.password != v2:
                raise ValidationError("密码错误")
            return self.cleaned_data
    
    from django.core.exceptions import NON_FIELD_ERRORS
    def login(request):
        if request.method == "GET":
            obj = LoginForm()
            return render(request,"login.html",{"obj":obj})
        else:
            obj = LoginForm(request.POST)
            all_code = None
            if obj.is_valid():
                print(obj.cleaned_data)
                return HttpResponse("登陆成功")
            else:
                print(obj.errors)
                all_code = obj.errors[NON_FIELD_ERRORS] 
                 # 在 template中没有 NON_FIELD_ERRORS,我只想到这个办法来传过去
            return render(request,"login.html",{"obj":obj,"all_code":all_code})
    

    你可能感兴趣的:(django 练习,分页,form 表单验证)