Django进阶篇

model

在程序中涉及到数据库的操作,比如操作mysql数据库,那么首先创建数据库,设计表结构和字段,用MYSQLdb来连接数据库,来操作数据库表中的数据,然后在业务层对处理的数据库数据通过html模板渲染到前端。

但django不是通过直接连接数据库来操作,而是通过ORM对象关系映射的方式构建数据库表和操作数据库数据。就像PHP中用activerecord操作数据库、java中的Hibernate或mybatis来操作数据库一样,都是通过ORM来操作数据库。

django中遵循 Code Frist 的原则,即:根据代码中定义的类来自动生成数据库表。

一、创建表

1、基本结构

from django.db import models
   
class userinfo(models.Model):
    name = models.CharField(max_length=30)
    email = models.EmailField()
    memo = models.TextField()

  字段

#如果在定义表结构的时候没有定义id字段,默认会自动产生一个id,也可以自己设定一个id
AutoField(Field)
- int自增列,必须填入参数 primary_key=True

BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True

SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767

IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647

PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647

PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767

BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807

BooleanField(Field)
- 布尔值类型

NullBooleanField(Field):
- 可以为空的布尔值

CharField(Field)
- 字符类型
- 必须提供max_length参数, max_length表示字符长度

TextField(Field)
- 文本类型

EmailField(CharField):
- 字符串类型,Django Admin以及ModelForm中提供验证机制

#这个只支持ipv4,不支持ipv6,已经做废掉
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]]

DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型

FloatField(Field)
- 浮点型

DecimalField(Field)
- 10进制小数
- 参数:
         max_digits,小数总长度
         decimal_places,小数位长度

BinaryField(Field)
- 二进制类型

  字段的参数

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

    error_messages      自定义错误信息(字典类型),从而定制想要显示的错误信息;
                        字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
                        如:{'null': "不能为空.", 'invalid': '格式错误'}

    validators          自定义错误验证(列表类型),从而定制想要的验证规则

2、连表结构

  • 一对多:models.ForeignKey(关联的表)
  • 多对多:models.ManyToManyField(关联的表)
  • 一对一:models.OneToOneField(关联的表)

 外键字段及其参数

ForeignKey(ForeignObject) # ForeignObject(RelatedField)
    to,                         # 要进行关联的表名
    to_field=None,              # 要关联的表中的字段名称
    on_delete=None,             # 当删除关联表中的数据时,当前表与其关联的行的行为
    related_name=None,          # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
     related_query_name=None,    # 反向操作时,使用的连接前缀,用于替换【表名】
    limit_choices_to=None,      # 在Admin或ModelForm中显示关联数据时,提供的条件:
    db_constraint=True          # 是否在数据库中创建外键约束
    parent_link=False           # 在Admin中是否显示关联数据



ManyToManyField(RelatedField)
    to,                         # 要进行关联的表名
    related_name=None,          # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
    related_query_name=None,    # 反向操作时,使用的连接前缀,用于替换【表名】
    limit_choices_to=None,      # 在Admin或ModelForm中显示关联数据时,提供的条件:
    symmetrical=None,           # 仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段
    through=None,               # 自定义第三张表时,使用字段用于指定关系表
    through_fields=None,        # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表
    db_constraint=True,         # 是否在数据库中创建外键约束
    db_table=None,              # 默认创建第三张表时,数据库中表的名称

二、操作表

1、基本操作

    # 增
    #增的方式一
    # models.Tb1.objects.create(c1='xx', c2='oo')  增加一条数据,可以接受字典类型数据 **kwargs

    #增的方式二 
    # obj = models.Tb1(c1='xx', c2='oo')
    # obj.save()

    # 查
    #
    # models.Tb1.objects.get(id=123)         # 获取单条数据,不存在则报错(不建议)
    # models.Tb1.objects.all()               # 获取全部
    # models.Tb1.objects.filter(name='goser') # 获取指定条件的数据

    # 删
    #
    # models.Tb1.objects.filter(name='goser').delete() # 删除指定条件的数据

    # 改
    # models.Tb1.objects.filter(name='goser').update(gender='0')  # 将指定条件的数据更新,均支持 **kwargs
    # obj = models.Tb1.objects.get(id=1)
    # obj.c1 = '111'
    # obj.save()                                                 # 修改单条数据

2、进阶操作(了不起的双下划线)

利用双下划线将字段和对应的操作连接起来

        # 获取个数
        #
        # models.Tb1.objects.filter(name='seven').count()

        # 大于,小于
        #
        # models.Tb1.objects.filter(id__gt=1)              # 获取id大于1的值
        # models.Tb1.objects.filter(id__gte=1)              # 获取id大于等于1的值
        # models.Tb1.objects.filter(id__lt=10)             # 获取id小于10的值
        # models.Tb1.objects.filter(id__lte=10)             # 获取id小于10的值
        # models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值

        # in
        #
        # models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
        # models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in

        # isnull
        # Entry.objects.filter(pub_date__isnull=True)

        # contains
        #
        # models.Tb1.objects.filter(name__contains="ven")
        # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
        # models.Tb1.objects.exclude(name__icontains="ven")

        # range
        #
        # models.Tb1.objects.filter(id__range=[1, 2])   # 范围bettwen and

        # 其他类似
        #
        # startswith,istartswith, endswith, iendswith,

        # order by
        #
        # models.Tb1.objects.filter(name='goser').order_by('id')    # asc
        # models.Tb1.objects.filter(name='goser').order_by('-id')   # desc

        # group by
        #
        # from django.db.models import Count, Min, Max, Sum
        # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
        # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"

        # limit 、offset
        #
        # models.Tb1.objects.all()[10:20]

        # regex正则匹配,iregex 不区分大小写
        #
        # Entry.objects.get(title__regex=r'^(An?|The) +')
        # Entry.objects.get(title__iregex=r'^(an?|the) +')

        # date
        #
        # Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
        # Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))

        # year
        #
        # Entry.objects.filter(pub_date__year=2005)
        # Entry.objects.filter(pub_date__year__gte=2005)

        # month
        #
        # Entry.objects.filter(pub_date__month=12)
        # Entry.objects.filter(pub_date__month__gte=6)

        # day
        #
        # Entry.objects.filter(pub_date__day=3)
        # Entry.objects.filter(pub_date__day__gte=3)

        # week_day
        #
        # Entry.objects.filter(pub_date__week_day=2)
        # Entry.objects.filter(pub_date__week_day__gte=2)

        # hour
        #
        # Event.objects.filter(timestamp__hour=23)
        # Event.objects.filter(time__hour=5)
        # Event.objects.filter(timestamp__hour__gte=12)

        # minute
        #
        # Event.objects.filter(timestamp__minute=29)
        # Event.objects.filter(time__minute=46)
        # Event.objects.filter(timestamp__minute__gte=29)

        # second
        #
        # Event.objects.filter(timestamp__second=31)
        # Event.objects.filter(time__second=2)
        # Event.objects.filter(timestamp__second__gte=31)

3、连表操作(了不起的双下划线)

  创建一个简单的单表,实现增、删、改、查

class User(models.Model):
            age = models.IntergerFiled()
            name = models.CharField(max_length=10)
创建表结构
        增
            models.User.objects.create(name='qianxiaohu',age=18)
            dic = {'name': 'xx', 'age': 19}
            models.User.objects.create(**dic)
            
            
            obj = models.User(name='goser',age=18)
            obj.save()
        删
            models.User.objects.filter(id=1).delete()
        改
            models.User.objects.filter(id__gt=1).update(name='rain',age=84)
            dic = {'name': 'xx', 'age': 19}
            models.User.objects.filter(id__gt=1).update(**dic)
        查
            models.User.objects.filter(id=1,name='root')
            models.User.objects.filter(id__gt=1,name='root')
            models.User.objects.filter(id__lt=1)
            models.User.objects.filter(id__gte=1)
            models.User.objects.filter(id__lte=1)
            
            models.User.objects.filter(id=1,name='root')
            dic = {'name': 'xx', 'age__gt': 19}
            models.User.objects.filter(**dic)
            
            v1 = models.Business.objects.all()
            # QuerySet ,内部元素都是对象.类似list
            
            # QuerySet ,内部元素都是字典,类似dict
            v2 = models.Business.objects.all().values('id','caption')
            # QuerySet ,内部元素都是元组
            v3 = models.Business.objects.all().values_list('id','caption')
        
            # 获取到的一个对象,如果不存在就报错,建议用filter().first()操作
            models.Business.objects.get(id=1)
            对象或者None = models.Business.objects.filter(id=1).first()
单表的增、删、改、查操作

  创建一个一对多的两张表

class Business(models.Model):
    # id
    caption = models.CharField(max_length=32)
    code = models.CharField(max_length=32,null=True,default="SA")
    # fk = models.ForeignKey('Foo')

class Host(models.Model):
    nid = models.AutoField(primary_key=True)
    hostname = models.CharField(max_length=32,db_index=True)
    ip = models.GenericIPAddressField(protocol="ipv4",db_index=True)
    port = models.IntegerField()
    b = models.ForeignKey(to="Business", to_field='id')
创建表结构
def host(request):
    if request.method == "GET":
        v1 = models.Host.objects.filter(nid__gt=0)
        v2 = models.Host.objects.filter(nid__gt=0).values('nid','hostname','b_id','b__caption')
        v3 = models.Host.objects.filter(nid__gt=0).values_list('nid','hostname','b_id','b__caption')

        b_list = models.Business.objects.all()

        return render(request, 'host.html', {'v1': v1,'v2': v2,'v3': v3,'b_list':b_list})

    elif request.method == "POST":

        h = request.POST.get('hostname')
        i = request.POST.get('ip')
        p = request.POST.get('port')
        b = request.POST.get('b_id')
        # models.Host.objects.create(hostname=h,
        #                            ip=i,
        #                            port=p,
        #                            b=models.Business.objects.get(id=b)
        #                            )
        models.Host.objects.create(hostname=h,
                                   ip=i,
                                   port=p,
                                   b_id=b
                                   )
        return redirect('/host')
Business和Host通过外键一对多关联操作

  创建一个多对多的表操作

class Host(models.Model):
                nid = models.AutoField(primary_key=True)
                hostname = models.CharField(max_length=32,db_index=True)
                ip = models.GenericIPAddressField(protocol="ipv4",db_index=True)
                port = models.IntegerField()
                b = models.ForeignKey(to="Business", to_field='id')

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

class HostToApp(models.Model):
                hobj = models.ForeignKey(to='Host',to_field='nid')
                aobj = models.ForeignKey(to='Application',to_field='id')
自定义创建第三张表来创建表结构
class Host(models.Model):
                nid = models.AutoField(primary_key=True)
                hostname = models.CharField(max_length=32,db_index=True)
                ip = models.GenericIPAddressField(protocol="ipv4",db_index=True)
                port = models.IntegerField()
                b = models.ForeignKey(to="Business", to_field='id')

            class Application(models.Model):
                name = models.CharField(max_length=32)
                r = models.ManyToManyField("Host")
通过默认自动创建第三张表的方式创建表结构

通过自定义的方式创建第三张表的方式操作类似于一对多的方式

而通过默认的方式创建第三张表的方式操作就要复杂一点,因为默认创建的第三张表是无法直接对其操作的,所以只有借多对多的外键来关联操作

#通过默认创建第三张表的方式来操作如下:
obj = Application.objects.get(id=1)

obj.r.add(1)
obj.r.add(2)
obj.r.add(2,3,4)
obj.r.add(*[1,2,3,4])

obj.r.remove(1)
obj.r.remove(2,4)
obj.r.remove(*[1,2,3])

#这种方式为外键r(id=1)对象对应的所有主机清除掉
obj.r.clear()

#这种方式为外键r(id=1)对象对应的主机添加上3,5,7,其他的主机删除
obj.r.set([3,5,7])

# 所有相关的主机对象“列表” QuerySet
obj.r.all()

4、F查询和Q查询

有时候仅仅靠单一的关键字查询满足不了查询需求,这时候就要Django提供的F查询和Q查询,这在生产环境中经常用到!

  F查询  使用查询条件的值,专门取对象中某列值的操作,这个一般对字段类型为integer的操作。

from django.db.models import F
models.Tb1.objects.update(num=F('num')+1)

  Q查询  通过Q查询来构建搜索条件

    from django.db.models import Q

    #1 Q对象(django.db.models.Q)可以对关键字参数进行封装,从而更好地应用多个查询
    q1=models.Book.objects.filter(Q(title__startswith='P')).all()
    print(q1)#[<Book: Python>, <Book: Perl>]

    # 2、可以组合使用&,|操作符,当一个操作符是用于两个Q的对象,它产生一个新的Q对象。
    Q(title__startswith='P') | Q(title__startswith='J')

    # 3、Q对象可以用~操作符放在前面表示否定,也可允许否定与不否定形式的组合
    Q(title__startswith='P') | ~Q(pub_date__year=2005)
    # 4、Q对象可以与关键字参数查询一起使用,不过一定要把Q对象放在关键字参数查询的前面。
    # 正确:
    Book.objects.get(
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
        title__startswith='P')

通过Ajax方式提交Form数据

模板中的form标签

<form id="edit_form" method="POST" action="/host">
		<input type="text" name="nid" style="display:none" />
		<input type="text" placeholder="主机名" name="hostname" />
		<input type="text" placeholder="IP" name="ip" />
		<input  type="text" placeholder="端口" name="port" />
		<select name="b_id">
			{% for op in b_list %}
			<option value="{{ op.id }}">{{ op.caption }}</option>
			{% endfor %}
		</select>
	<a id="ajax_submit_edit" >确认编辑</a>
</form>

ajax书写格式:

$('#ajax_submit').click(function(){
	$.ajax({
		url: "/test_ajax",
		type: 'POST',
		//data: {'hostname': $('#host').val(), 'ip': $('#ip').val(), 'port': $('#port').val(), 'b_id': $('#sel').val()},
		data: $('#add_form').serialize(),
		success: function(data){
			var obj = JSON.parse(data);
			if(obj.status){
				location.reload();
			}else{
				$('#erro_msg').text(obj.error);
			}
		}
	})
});
上面的代码中JSON.parse(data)可以去掉,只要加上dataType: 'JSON'就行了,其代表也就是JSON.parse(data);还要加上traditional: true来阻止深度序列化,因为ajax提交的data默认是数字或字符串,如果前端提交的是列表的数据(比如select标签和checkbox标签),那么后端是获取不到前端提交的list数据,所以在ajax提交表单时应加上traditional: true。所以上面的ajax的优化如下:

 

$('#ajax_submit').click(function(){
	$.ajax({
		url: "/test_ajax",
		type: 'POST',
		dataType: 'JSON',
		traditional: true,
		//data: {'hostname': $('#host').val(), 'ip': $('#ip').val(), 'port': $('#port').val(), 'b_id': $('#sel').val()},
		data: $('#add_form').serialize(),
		success: function(data){
			if(data.status){
				location.reload();
			}else{
				$('#erro_msg').text(data.error);
			}
		}
	})
});

后台view处理方式为:

def test_ajax(request):

    ret = {'status': True, 'error': None, 'data': None}
    try:
        h = request.POST.get('hostname')
        i = request.POST.get('ip')
        p = request.POST.get('port')
        b = request.POST.get('b_id')
        if h and len(h) > 5:
            models.Host.objects.create(hostname=h,
                                           ip=i,
                                           port=p,
                                           b_id=b)
        else:
            ret['status'] = False
            ret['error'] = "太短了"
    except Exception as e:
        ret['status'] = False
        ret['error'] = '请求错误'
    return HttpResponse(json.dumps(ret))

 分页

一、django自带的分页

django框架有自带的分页功能,可以直接利用它自带的分页功能来做页面的分页展现

django的分页官方文档为:https://docs.djangoproject.com/en/1.10/topics/pagination/

  1、django的简单示例:

>>> from django.core.paginator import Paginator
>>> objects = ['john', 'paul', 'george', 'ringo']
>>> p = Paginator(objects, 2)

>>> p.count
4
>>> p.num_pages
2
>>> type(p.page_range)  # `<type 'rangeiterator'>` in Python 2.
<class 'range_iterator'>
>>> p.page_range
range(1, 3)

>>> page1 = p.page(1)
>>> page1
<Page 1 of 2>
>>> page1.object_list
['john', 'paul']

>>> page2 = p.page(2)
>>> page2.object_list
['george', 'ringo']
>>> page2.has_next()
False
>>> page2.has_previous()
True
>>> page2.has_other_pages()
True
>>> page2.next_page_number()
Traceback (most recent call last):
...
EmptyPage: That page contains no results
>>> page2.previous_page_number()
1
>>> page2.start_index() # The 1-based index of the first item on this page
3
>>> page2.end_index() # The 1-based index of the last item on this page
4
View Code

  2、在后端

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render

def listing(request):
    contact_list = Contacts.objects.all()
    paginator = Paginator(contact_list, 25) # Show 25 contacts per page

    page = request.GET.get('page')
    try:
        contacts = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        contacts = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        contacts = paginator.page(paginator.num_pages)

    return render(request, 'list.html', {'contacts': contacts})

  3、前端

<div class="pagination">
    <span class="step-links">
        {% if contacts.has_previous %}
            <a href="?page={{ contacts.previous_page_number }}">previous</a>
        {% endif %}

        <span class="current">
            Page {{ contacts.number }} of {{ contacts.paginator.num_pages }}.
        </span>

        {% if contacts.has_next %}
            <a href="?page={{ contacts.next_page_number }}">next</a>
        {% endif %}
    </span>
</div>

通过这种简单的Django自带的分页功能就可以实现简单的分页功能,当然这样的分页还需要进一步处理,比如加上上一页、下一页、首页、尾页等。。。。

二、自定义分页

如果不是在django框架中定义分页的话,这时候就要自己定义一个分页功能,比如在Flash  tonardo等框架中就要自定义分页。

需求:

  需要在页面上显示分页的页面。如:[上一页][1][2][3][4][5][下一页]

分析: 

      1、设定每页显示数据条数

      2、用户输入页码(第一页、第二页...)

      3、设定显示多少页号

      4、获取当前数据总条数

      5、根据设定显示多少页号和数据总条数计算出,总页数

      6、根据设定的每页显示条数和当前页码,计算出需要取数据表的起始位置

      7、在数据表中根据起始位置取值,页面上输出数据

      8、输出分页html,如:[上一页][1][2][3][4][5][下一页]

分页实例:

  1、写一个分页工具类:

__author__ = 'Administrator'
from django.utils.safestring import mark_safe


class Page:
    def __init__(self, current_page, data_count, per_page_count=10, pager_num=7):
        self.current_page = current_page
        self.data_count = data_count
        self.per_page_count = per_page_count
        self.pager_num = pager_num

    @property
    def start(self):
        return (self.current_page - 1) * self.per_page_count

    @property
    def end(self):
        return self.current_page * self.per_page_count

    @property
    def total_count(self):
        v, y = divmod(self.data_count, self.per_page_count)
        if y:
            v += 1
        return v

    def page_str(self, base_url):
        page_list = []

        if self.total_count < self.pager_num:
            start_index = 1
            end_index = self.total_count + 1
        else:
            if self.current_page <= (self.pager_num + 1) / 2:
                start_index = 1
                end_index = self.pager_num + 1
            else:
                start_index = self.current_page - (self.pager_num - 1) / 2
                end_index = self.current_page + (self.pager_num + 1) / 2
                if (self.current_page + (self.pager_num - 1) / 2) > self.total_count:
                    end_index = self.total_count + 1
                    start_index = self.total_count - self.pager_num + 1

        if self.current_page == 1:
            prev = '<a class="page" href="javascript:void(0);">上一页</a>'
        else:
            prev = '<a class="page" href="%s?p=%s">上一页</a>' % (base_url, self.current_page - 1,)
        page_list.append(prev)

        for i in range(int(start_index), int(end_index)):
            if i == self.current_page:
                temp = '<a class="page active" href="%s?p=%s">%s</a>' % (base_url, i, i)
            else:
                temp = '<a class="page" href="%s?p=%s">%s</a>' % (base_url, i, i)
            page_list.append(temp)

        if self.current_page == self.total_count:
            nex = '<a class="page" href="javascript:void(0);">下一页</a>'
        else:
            nex = '<a class="page" href="%s?p=%s">下一页</a>' % (base_url, self.current_page + 1,)
        page_list.append(nex)

        jump = """
        <input type='text'  /><a onclick='jumpTo(this, "%s?p=");'>GO</a>
        <script>
            function jumpTo(ths,base){
                var val = ths.previousSibling.value;
                location.href = base + val;
            }
        </script>
        """ % (base_url,)

        page_list.append(jump)

        page_str = mark_safe("".join(page_list))

        return page_str
分页工具类pagination.py

然后再view中调用这个分页工具类

from  utils import pagination
LIST = []
for i in range(500):
    LIST.append(i)

def user_list(request):
    current_page = request.GET.get('p', 1)
    current_page = int(current_page)

    val = request.COOKIES.get('per_page_count',10)
    val = int(val)
    page_obj = pagination.Page(current_page,len(LIST),val)

    data = LIST[page_obj.start:page_obj.end]

    page_str = page_obj.page_str("/user_list/")

    return render(request, 'user_list.html', {'li': data,'page_str': page_str})

自定义分页总结:需要做三件事

  • 创建处理分页数据的类
  • 根据分页数据获取数据
  • 输出分页HTML,即:[上一页][1][2][3][4][5][下一页]

Cookie

1、获取Cookie:

request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
    参数:
        default: 默认值
           salt: 加密盐
        max_age: 后台控制过期时间

2、设置Cookie:

rep = HttpResponse(...) 或 rep = render(request, ...)
 
rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密盐',...)
    参数:
        key,              键
        value='',         值
        max_age=None,     超时时间
        expires=None,     超时时间(IE requires expires, so set it if hasn't been already.)
        path='/',         Cookie生效的路径,/ 表示根路径,特殊的:跟路径的cookie可以被任何url的页面访问
        domain=None,      Cookie生效的域名
        secure=False,     https传输
        httponly=False    只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)

由于cookie保存在客户端的电脑上,所以,JavaScript和jquery也可以操作cookie。所以建议用session,因为session的数据是放在服务器上的。

<script src='/static/js/jquery.cookie.js'></script>
$.cookie("list_pager_num", 30,{ path: '/' });

 Session

要使用session的话,客户端浏览器必须开启cookie功能,因为session基于cookie定义的,没有cookie,session形同虚设。Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:

  • 数据库(默认)
  • 缓存
  • 文件
  • 缓存+数据库
  • 加密cookie

1、数据库Session

a. 配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)
     
    SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
    SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)
    SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)
    SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)
    SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)
    SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默认)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)
    SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次请求都保存Session,默认修改之后才保存(默认)
 
 
 
b. 使用
 
    def index(request):
        # 获取、设置、删除Session中数据
        request.session['k1']
        request.session.get('k1',None)
        request.session['k1'] = 123
        request.session.setdefault('k1',123) # 存在则不设置
        del request.session['k1']
 
        # 所有 键、值、键值对
        request.session.keys()
        request.session.values()
        request.session.items()
        request.session.iterkeys()
        request.session.itervalues()
        request.session.iteritems()
 
 
        # 用户session的随机字符串
        request.session.session_key
 
        # 将所有Session失效日期小于当前日期的数据删除
        request.session.clear_expired()
 
        # 检查 用户session的随机字符串 在数据库中是否
        request.session.exists("session_key")
 
        # 删除当前用户的所有Session数据
        request.session.delete("session_key")
 
        #清空session记录,也就是注销登录
        request.session.clear()

        request.session.set_expiry(value)
            * 如果value是个整数,session会在些秒数后失效。
            * 如果value是个datatime或timedelta,session就会在这个时间后失效。
            * 如果value是0,用户关闭浏览器session就会失效。
            * 如果value是None,session会依赖全局session失效策略。

2、缓存Session

a. 配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
    SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
    settings中其他配置同上




b. 使用
 
    同上

3、文件Session

a. 配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
    SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()
    settings中其他配置同上
b. 使用 同上

4、缓存+数据库Session

 

数据库用于做持久化,缓存用于提高效率
 
a. 配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎
 
b. 使用
 
    同上

5、加密cookie Session

a. 配置 settings.py
     
    SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎
 
b. 使用
 
    同上

如果想对某个用户设置特殊的session超时时间的话,那么这个用户将在这个时间后session便失效,而其他用户依然使用全局的过期时间(默认2周)

def login(request):
    # from django.conf import settings
    # print(settings.CSRF_HEADER_NAME)
    # HTTP_X_CSRFTOKEN
    # X-CSRFtoken

    if request.method == "GET":
        return render(request,'login.html')
    elif request.method == "POST":
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        if user == 'root' and pwd == "123":
            # session中设置值
            request.session['username'] = user
            request.session['is_login'] = True
            if request.POST.get('rmb',None) == '1':
                # 超时时间
                request.session.set_expiry(10)
            return redirect('/index/')
        else:
            return render(request,'login.html')

缓存

由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache中,5分钟内再有人来访问时,则不再去执行view中的操作,而是直接从内存或者Redis中之前缓存的内容拿到,并返回。

Django中提供了6种缓存方式:

  • 开发调试
  • 内存
  • 文件
  • 数据库
  • Memcache缓存(python-memcached模块)
  • Memcache缓存(pylibmc模块)

1、配置

a、开发调试

    # 此为开始调试用,实际内部不做任何操作
    # 配置:
        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.dummy.DummyCache',     # 引擎
                'TIMEOUT': 300,                                               # 缓存超时时间(默认300,None表示永不过期,0表示立即过期)
                'OPTIONS':{
                    'MAX_ENTRIES': 300,                                       # 最大缓存个数(默认300)
                    'CULL_FREQUENCY': 3,                                      # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
                },
                'KEY_PREFIX': '',                                             # 缓存key的前缀(默认空)
                'VERSION': 1,                                                 # 缓存key的版本(默认1)
                'KEY_FUNCTION' 函数名                                          # 生成key的函数(默认函数会生成为:【前缀:版本:key】)
            }
        }


    # 自定义key
    def default_key_func(key, key_prefix, version):
        """
        Default function to generate keys.

        Constructs the key used by all other methods. By default it prepends
        the `key_prefix'. KEY_FUNCTION can be used to specify an alternate
        function with custom key making behavior.
        """
        return '%s:%s:%s' % (key_prefix, version, key)

    def get_key_func(key_func):
        """
        Function to decide which key function to use.

        Defaults to ``default_key_func``.
        """
        if key_func is not None:
            if callable(key_func):
                return key_func
            else:
                return import_string(key_func)
        return default_key_func
View Code

b、内存

    # 此缓存将内容保存至内存的变量中
    # 配置:
        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
                'LOCATION': 'unique-snowflake',
            }
        }

    # 注:其他配置同开发调试版本
View Code

c、文件

    # 此缓存将内容保存至文件
    # 配置:

        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
                'LOCATION': '/var/tmp/django_cache',
            }
        }
    # 注:其他配置同开发调试版本
View Code

d、数据库

   # 此缓存将内容保存至数据库

    # 配置:
        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
                'LOCATION': 'my_cache_table', # 数据库表
            }
        }

    # 注:执行创建表命令 python manage.py createcachetable

e、Memcache缓存(python-memcached模块)

# 此缓存使用python-memcached模块连接memcache

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': '127.0.0.1:11211',
        }
    }

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': 'unix:/tmp/memcached.sock',
        }
    }   

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': [
                '172.19.26.240:11211',
                '172.19.26.242:11211',
            ]
        }
    }
View Code

f、Memcache缓存(pylibmc模块)

    # 此缓存使用pylibmc模块连接memcache
    
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
            'LOCATION': '127.0.0.1:11211',
        }
    }

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
            'LOCATION': '/tmp/memcached.sock',
        }
    }   

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
            'LOCATION': [
                '172.19.26.240:11211',
                '172.19.26.242:11211',
            ]
        }
    }

2、应用

a. 全站使用

   使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存

    MIDDLEWARE = [
        'django.middleware.cache.UpdateCacheMiddleware',
        # 其他中间件...
        'django.middleware.cache.FetchFromCacheMiddleware',
    ]

    CACHE_MIDDLEWARE_ALIAS = ""
    CACHE_MIDDLEWARE_SECONDS = ""
    CACHE_MIDDLEWARE_KEY_PREFIX = ""
使用中间件全站使用

b. 单独视图缓存

from django.views.decorators.cache import cache_page

@cache_page(10) #表示10秒钟缓存一次
def cache(request):
    import time
    ctime = time.time()
    return render(request, 'cache.html', {'ctime': ctime})

c、局部视图使用

缓存还可以细粒化,用在html上的局部缓存

{% load cache %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>{{ ctime }}</h1>
    <h1>{{ ctime }}</h1>

     {% cache 10 c1 %}
        <h1>{{ ctime }}</h1>
     {% endcache %}
</body>
</html>

跨站请求伪造

一、简介

django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。而对于django中设置防跨站请求伪造功能有分为全局和局部。

全局:

  中间件 django.middleware.csrf.CsrfViewMiddleware

局部:

  • @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
  • @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。

可以将全局的django.middleware.csrf.CsrfViewMiddleware在settings中注掉,再用@csrf_protect对需要做csrf的view视图做装饰,这个对csrf要求不多的web服务用

也可以在全局crsf生效的情况下,对极少数的view视图不做csrf,这种情况下在view视图做@csrf_exempt装饰,这个对csrf大量使用的web服务用的比较多。

示例

#这个示例表示在全局csrf失效的情况下,对某些view做csrf操作
@csrf_protect
def index(request):
    # session中获取值
    if request.session.get('is_login',None):
        return render(request,'index.html',{'username': request.session['username']})
    else:
        return HttpResponse('认证没通过')

注:from django.views.decorators.csrf import csrf_exempt,csrf_protect

二、应用

1、普通表单

veiw中设置返回值:
return render(request, 'xxx.html', data)
  
html中设置Token:
<form action="/login/" method="POST">
	{% csrf_token %}
	<input type="text" name="user" />
	<input type="text" name="pwd" />
	<input type="checkbox" name="rmb" value="1" /> 10秒免登录
	<input type="submit" value="提交" />
	<input id="btn1" type="button" value="按钮" />
	<input id="btn2" type="button" value="按钮" />
</form>

2、Ajax

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    {% csrf_token %}
  
    <input type="button" onclick="Do();"  value="Do it"/>
  
    <script src="/static/plugin/jquery/jquery-1.8.0.js"></script>
    <script src="/static/plugin/jquery/jquery.cookie.js"></script>
    <script type="text/javascript">
        var csrftoken = $.cookie('csrftoken');
  
        function csrfSafeMethod(method) {
            // these HTTP methods do not require CSRF protection
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
        }
        $.ajaxSetup({
            beforeSend: function(xhr, settings) {
                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
                }
            }
        });
        function Do(){
  
            $.ajax({
                url:"/app01/test/",
                data:{id:1},
                type:'POST',
                success:function(data){
                    console.log(data);
                }
            });
  
        }
    </script>
</body>
</html>

信号

在某件事情触发之前后之后要做某种操作,这时候就要用信号的处理

1、内置信号:

from django.core.signals import request_finished
from django.core.signals import request_started
from django.core.signals import got_request_exception

from django.db.models.signals import class_prepared
from django.db.models.signals import pre_init, post_init
from django.db.models.signals import pre_save, post_save
from django.db.models.signals import pre_delete, post_delete
from django.db.models.signals import m2m_changed
from django.db.models.signals import pre_migrate, post_migrate

from django.test.signals import setting_changed
from django.test.signals import template_rendered

from django.db.backends.signals import connection_created

对于Django内置的信号,仅需注册指定信号,当程序执行相应操作时,自动触发注册函数:

  在工程中创建一个sg.py信号文件

from django.db.models.signals import pre_save, post_save
def f1(sender, **kwargs):
        print("xxoo_callback")
        # print(sender,kwargs)

pre_save.connect(f1)

#注:这样在保存数据库之前就会触发这个信号

  然后再与工程名相同的文件夹下的__init__.py中导入这个模块

import  sg.py

  最后在view中就可以直接使用这个信号了,因为在程序启动后会自动加载与工程名相同的文件夹下的__init__.py的模块

def signal(reuqest):
    from app01 import models

    obj = models.UserInf(user='root')
    print('end')
    obj.save()
    return HttpResponse('ok')

#注:在userinf表添加一行数据前触发信号


#在控制台上输出的结果为:
xxoo_callback
end

 

2、自定义信号

  a. 定义信号

import django.dispatch
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])

  b. 注册信号

def callback(sender, **kwargs):
    print("callback")
    print(sender,kwargs)
 
pizza_done.connect(callback)

  c. 触发信号

def signal(reuqest):
    from app01 import models

    obj = models.UserInf(user='root')
    print('end')
    obj.save()

    from sg import pizza_done

    pizza_done.send(sender="asdfasdf",toppings=123, size=456)

    return HttpResponse('ok')

Form

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内容后缀
 
 
#上面的Filed类是下面所有字段类型的基类,也就是说下面的字段类型中参数继承了Field类
CharField(Field)
    max_length=None,             最大长度
    min_length=None,             最小长度
    strip=True                   是否移除用户输入空白
 
IntegerField(Field)
    max_value=None,              最大值
    min_value=None,              最小值
 
FloatField(IntegerField)
    ...
 
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
 
DurationField(Field)            时间间隔:%d %H:%M:%S.%f
    ...
 
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)
 
URLField(Field)
    ...
 
 
BooleanField(Field)  
    ...
 
NullBooleanField(BooleanField)
    ...
 
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类型
Django内置字段类型如下
TextInput(Input)
NumberInput(TextInput)
EmailInput(TextInput)
URLInput(TextInput)
PasswordInput(TextInput)
HiddenInput(TextInput)
Textarea(Widget)
DateInput(DateTimeBaseInput)
DateTimeInput(DateTimeBaseInput)
TimeInput(DateTimeBaseInput)
CheckboxInput
Select
NullBooleanSelect
SelectMultiple
RadioSelect
CheckboxSelectMultiple
FileInput
ClearableFileInput
MultipleHiddenInput
SplitDateTimeWidget
SplitHiddenDateTimeWidget
SelectDateWidget
对于widget用到的Django内置插件

a、在view中定义一个Form

from django import forms
from django.forms import widgets
from django.forms import fields
class FM(forms.Form):
    # 字段本身只做验证
    user = fields.CharField(
        error_messages={'required': '用户名不能为空.'},
        widget=widgets.Textarea(attrs={'class': 'c1'}),
        label="用户名",
        )
    pwd = fields.CharField(
        max_length=12,
        min_length=6,
        error_messages={'required': '密码不能为空.', 'min_length': '密码长度不能小于6', "max_length": '密码长度不能大于12'},
        widget=widgets.PasswordInput(attrs={'class': 'c2'})
    )
    email = fields.EmailField(error_messages={'required': '邮箱不能为空.','invalid':"邮箱格式错误"})

    f = fields.FileField()

    # p = fields.FilePathField(path='app01')

    city1 = fields.ChoiceField(
        choices=((0,'上海'),(1,'广州'),(2,'东莞'))
    )
    city2 = fields.MultipleChoiceField(
        choices=((0,'上海'),(1,'广州'),(2,'东莞'))
    )

b、view中业务处理函数定义

from app01 import models
def fm(request):
    if request.method == "GET":
        # 从数据库中吧数据获取到
        dic = {
            "user": 'r1',
            'pwd': '123123',
            'email': 'sdfsd',
            'city1': 1,
            'city2': [1,2]
        }
        obj = FM(initial=dic)
        return render(request,'fm.html',{'obj': obj})
    elif request.method == "POST":
        # 获取用户所有数据
        # 每条数据请求的验证
        # 成功:获取所有的正确的信息
        # 失败:显示错误信息
        obj = FM(request.POST)
        r1 = obj.is_valid()
        if r1:
            # obj.cleaned_data
            models.UserInf.objects.create(**obj.cleaned_data)
        else:
            # ErrorDict
            # print(obj.errors.as_json())
            # print(obj.errors['user'][0])
            return render(request,'fm.html', {'obj': obj})
        return render(request,'fm.html')

c、前端fm.html定义

  方式一、

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <form action="/fm/" method="POST">
        {% csrf_token %}
        <p>{{ obj.user.label }} <input type='text' name = 'user' />{{ obj.errors.user.0 }}</p>
        <p><input type='text' name = 'pwd' /> {{ obj.errors.pwd.0 }}</p>
        <p><input type='text' name = 'email' /> {{ obj.errors.email.0 }}</p>
        <p><input type='text' name = 'f' />{{ obj.errors.f.0 }}</p>
        <input type='select' name='city1' />
        <input type='select' name='city2' />
        <input type="submit" value="提交" />
    </form>
</body>
</html>

  方式二、

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <form action="/fm/" method="POST">
        {% csrf_token %}
        <p>{{ obj.user.label }} {{ obj.user }} {{ obj.errors.user.0 }}</p>
        <p>{{ obj.pwd }} {{ obj.errors.pwd.0 }}</p>
        <p>{{ obj.email }}{{ obj.errors.email.0 }}</p>
        <p>{{ obj.f }}{{ obj.errors.f.0 }}</p>
        {{ obj.city1 }}
        {{ obj.city2 }}
        <input type="submit" value="提交" />
    </form>
</body>
</html>

 特殊的单选或多选时,实现数据源实时更新

#model部分
from django.db import models

class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    email = models.EmailField(max_length=32)

    def __str__(self):
        return self.username

#view部分
class LoveForm(forms.Form):
    price = fields.IntegerField()
    user_id = fields.IntegerField(
        # widget=widgets.Select(choices=[(0,'alex'),(1,'刘皓宸'),(2,'杨建'),])
        widget=widgets.Select()
    )

    user_id2 = ModelChoiceField(
        queryset=models.UserInfo.objects.all(),
        to_field_name='id'
    )

    def __init__(self,*args,**kwargs):
        # super()拷贝所有的静态字段,复制给self.fields
        super(LoveForm,self).__init__(*args,**kwargs)
        self.fields['user_id'].widget.choices = models.UserInfo.objects.values_list('id', 'username')

def love(request):
   #当用户get请求后,对loveForm初始化执行__init__方法,就会实时跟新select选择框的数据源
    obj = LoveForm() 
    return render(request,'love.html',{'obj':obj})

#template
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>单选或多选数据源实时更新</h1>
    <p>价格:{{ obj.price }}</p>
    <p>选择框:{{ obj.user_id }}</p>
</body>
</html>

序列化

关于Django中自带的序列化主要应用在将数据库中检索的数据(指的是包含对象的Queryset类型)返回给客户端用户,如果用json直接对Queryset做序列化,程序将直接报错,因为json是无法识别Queryset类型的。

当然如果在查询数据库的时候使用value或value_list的话,那么取到的数据就是包含内容为字典或元组的Queryset类型,然后对包含内容为字典或元组的Queryset做list转换成一般的list类型,这样json就可以直接对这个转换的list类型做序列化了(json.dump(list(Queryset类型数据))),这种使用特别在Ajax请求一般返回为Json格式。

1、serializers

from django.core import serializers
 
ret = models.BookType.objects.all()
 
data = serializers.serialize("json", ret)

2、json.dumps

import json
 
#ret = models.BookType.objects.all().values('caption')
ret = models.BookType.objects.all().values_list('caption')
 
ret=list(ret)
 
result = json.dumps(ret)

  

posted @ 2017-07-26 14:56  goser  阅读(192)  评论(0)    收藏  举报