首页 > 技术文章 > django-models模型(Model数据库表的增删改查,QuerySet操作、ModelForm登录注册验证)

daemon-czk 2018-10-18 11:08 原文

模型是数据唯一而且准确的信息来源。它包含正在储存的数据的重要字段和行为。一般来说,每一个模型都映射一个数据库表。

参考网址:https://docs.djangoproject.com/zh-hans/2.0/topics/db/

一、创建表

示例:

1、创建类
  models文件中写代码:
  #在db.sqlite3中会创建app001_userinfo名的数据库表,
  class UserInfo(models.Model):  #django 会自动创建id列,是自增的,还是主键,用户名列,字符串类型,指定长度;
    username=models.CharField(max_length=32)
    password=models.CharField(max_length=64)

2、在settings中的installed_app加上app的名字添加进去:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app001',
]
3、 python manage.py makemigrations,注意:在每个app里面的urls中如果都有url(r'^admin/', admin.site.urls),那么会出现admin不是唯一的错误信息。

4、 python manage.py migrate
5、可以在settings的DATABASES中修改存储的数据库连接到自己的mysql,默认是连接到django知道的db.sqlite3数据库

注意:
django默认是使用MySQLdb模块连接mysql,但是python3中没有这个模块,所以让其用pymysql连接的方法:
主动修改pymysql,在工程名文件同名的文件夹下面的__init__.py中添加如下代码:
  import pymysql
  pymysql install_as_MySQLdb()

 

1、基本结构

1
2
3
4
5
6
from django.db import models
   
class userinfo(models.Model):
    name = models.CharField(max_length=30)
    email = models.EmailField()
    memo = models.TextField()

数据类型:

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

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

注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models

class UserInfo(models.Model):
# 自动创建一个列名为id的且为自增的整数列
username = models.CharField(max_length=32)

class Group(models.Model):
# 自定义自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)

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

PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647

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

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

自定义无符号整数字段

class UnsignedIntegerField(models.IntegerField):
def db_type(self, connection):
return 'integer UNSIGNED'

PS: 返回值为字段在数据库中的属性,Django字段默认的值为:
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',

BooleanField(Field)
- 布尔值类型

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

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

TextField(Field)
- 文本类型

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

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 数据库中字段【年】部分是否可以建立唯一索引

auto_now 更新数据的时间,注意:django不支持first.updata(),这种方式,只能按照取值,赋新值,然后save()的方式执行才会更新

auto_now_add  创建数据的时候这一列不用加,django会自动添加

verbose_name  django的Admin中显示的字段名称,原来的是英文的,可以修改成中文显示
blank 在django的Admin中是否允许用户输入为空
editable django的Admin中是否可以编辑
help_text Admin中该字段的提示信息
choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作,在django的admin中显示下拉框,避免连表查询
如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)

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

validators 自定义错误验证(列表类型),从而定制想要的验证规则
from django.core.validators import RegexValidator
from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
如:
test = models.CharField(
max_length=32,
error_messages={
'c1': '优先错信息1',
'c2': '优先错信息2',
'c3': '优先错信息3',
},
validators=[
RegexValidator(regex='root_\d+', message='错误了', code='c1'),
RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'),
EmailValidator(message='又错误了', code='c3'), ]
)

参数

class UserInfo(models.Model):
nid = models.AutoField(primary_key=True)
username = models.CharField(max_length=32)
class Meta:
# 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
db_table = "table_name"

# 联合索引
index_together = [
("pub_date", "deadline"),
]

# 联合唯一索引
unique_together = (("driver", "restaurant"),)

# admin中显示的表名称
verbose_name

# verbose_name加s
verbose_name_plural

更多:https://docs.djangoproject.com/en/1.10/ref/models/options/

2、连表结构

通过自动创建的关系表无法对第三张表直接进行操作,

当用系统默认方式创建多对多关系的时候,也就是ManytoManyfield(),如果要操作第三张表,name必须通过obj先查询到要操作的第一张表,然后针对第一张表的obj里面的多对多关系r进行增删改查,假设obj查找到的是id=1的一个类表,那么要在这个类上对应多对多的关系,可以用add增加对应,可以是1对1,也可以是1对1,1对2等等,remove删除关系也是向通道里,注意上面的add和set操作列表的时候,add后面列表前加了*,而set后面的列表前面没有加*好,set是修改的意思,原先的对应关系已经失效,而add是在原有已经映射的关系上再增加关系

注意:多对多实现过程中弹窗实例演示(用到Ajax),

html端js代码:

如果data后面获取的是列表的话,要加上参数traditional:true,这样后台通过getlist就可以拿到列表数据了,用Ajax弹窗的时候可以增加datatype:'JSON',这一列,那么下面的函数里面就不用再json转换成字符串了

views端代码:

odels.ForeignKey('其他表',to_field='id'),可以实现一个表查询另一个表里面的数据,直接.group就可以,如果是.group_id那查询的还是本表里面对应的列,所以通过.group可以一直查询下去,比如,一个表对应一个group,group又对应多个group2。。。。这样连续的关系表操作都可以通过.group进行操作下去。

 

一对一表结构:OneToOneField(ForeignKey)

参数:

  • to, # 要进行关联的表名
  • to_field=None # 要关联的表中的字段名称
  • on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为

###### 对于一对一 ######
# 1. 一对一其实就是 一对多 + 唯一索引
# 2.当两个类之间有继承关系时,默认会创建一个一对一字段
# 如下会在A表中额外增加一个c_ptr_id列且唯一:
class C(models.Model):
  nid = models.AutoField(primary_key=True)
  part = models.CharField(max_length=12)

class A(C):
  id = models.AutoField(primary_key=True)
  code = models.CharField(max_length=1)

 

一对多表结构:ForeignKey(ForeignObject) # ForeignObject(RelatedField)

参数:

  • to, # 要进行关联的表名  
  • to_field=None, # 要关联的表中的字段名称
  • on_delete=  , # 当删除关联表中的数据时,当前表与其关联的行的行为,后面接下面的可选参数:   重要
    1.   - models.CASCADE,删除关联数据,与之关联也删除,django2.xx版本默认要加上这一句
    2.   - models.DO_NOTHING,删除关联数据,引发错误IntegrityError,由数据库级别抛出的异常
    3.   - models.PROTECT,删除关联数据,引发错误ProtectedError,由django级别抛出的异常
    4.   - models.SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
    5.   - models.SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
    6.   - models.SET,删除关联数据:a. 与之关联的值设置为指定值,设置:models.SET(值)b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)

        on_delete示例:

        class MyModel(models.Model):
            user = models.ForeignKey(
            to="User",
            to_field="id"
            on_delete=models.SET(func),)

  • related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()   重要,特别是自关联的时候防止名字冲突
  • related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')

下面的参数只要在admin中用到

  • limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件: 重要
  1. # 如:
    1. - limit_choices_to={'nid__gt': 5}  显示多少条信息,此处是nid大于5的显示出来
    2. - limit_choices_to=lambda : {'nid__gt': 5}
    3. from django.db.models import Q
    4. - limit_choices_to=Q(nid__gt=10)
    5. - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
    6. - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
  • db_constraint=True # 是否在数据库中创建外键约束,如果等于false就是说在django中创建了外键约束,但是在数据库中还是独立的两张表,没有外键约束,如果等于true,那么在django和数据库创建的表都有外键约束;;
  • parent_link=False # 在Admin中是否显示关联数据,如果为true才显示数据库中关联的数据

 

多对多表结构:ManyToManyField(RelatedField)

参数:

  • to, # 要进行关联的表名
  • related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()     重要
  • related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
  • limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件:# 如: 
  1. - limit_choices_to={'nid__gt': 5}
  2. - limit_choices_to=lambda : {'nid__gt': 5}

from django.db.models import Q

  • - limit_choices_to=Q(nid__gt=10)
  • - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
  • - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
  • symmetrical=None, # 仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段

# 做如下操作时,不同的symmetrical会有不同的可选字段
models.BB.objects.filter(...)

# 可选字段有:code, id, m1
class BB(models.Model):

  code = models.CharField(max_length=12)
  m1 = models.ManyToManyField('self',symmetrical=True)

# 可选字段有: bb, code, id, m1
class BB(models.Model):

  code = models.CharField(max_length=12)
  m1 = models.ManyToManyField('self',symmetrical=False)

through=None, # 自定义第三张表时,使用字段用于指定关系表,通过这种方法创建关系的时候不能用add.remove等增删改操作,但是能查询;重要
through_fields=['指定字段1','指定字段2'...], # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表 重要

 

through演示:


from django.db import models

class Person(models.Model):
  name = models.CharField(max_length=50)

class Group(models.Model):
  name = models.CharField(max_length=128)
  members = models.ManyToManyField(
            Person,
          through='Membership',
          through_fields=('group', 'person'),
                  )

class Membership(models.Model):
  group = models.ForeignKey(Group, on_delete=models.CASCADE)
  person = models.ForeignKey(Person, on_delete=models.CASCADE)
  inviter = models.ForeignKey(
          Person,
          on_delete=models.CASCADE,
          related_name="membership_invites",
              )
  invite_reason = models.CharField(max_length=64)

 

  •   db_constraint=True, # 是否在数据库中创建外键约束
  •   db_table=None, # 默认创建第三张表时,数据库中表的名称字段以及参数

应用场景:

一对多:当一张表中创建一行数据时,有一个单选的下拉框(可以被重复选择)
例如:创建用户信息时候,需要选择一个用户类型【普通用户】【金牌用户】【铂金用户】等。

多对多:在某表中创建一行数据是,有一个可以多选的下拉框
例如:创建用户信息,需要为用户指定多个爱好

一对一:在某表中创建一行数据时,有一个单选的下拉框(下拉框中的内容被用过一次就消失了
例如:原有含10列数据的一张表保存相关信息,经过一段时间之后,10列无法满足需求,需要为原来的表再添加5列数据

 

二、操作表

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() # 获取全部,返回的是一个QuerySet类型列表,每一个都是TB1的对象,可以通过for循环提取数据
# models.Tb1.objects.filter(name='seven') # 获取指定条件的数据,相当于mysql中的where查询条件,也要通过for循环提取

# models.Tb1.objects.values_list('id','caption'...) 拿到的query对象里面的数据是元组的形式,只能用索引0,1,2...等

在跨表操作的时候,可以通过.进行跨表,通过[]也可以去元素:v[0].b.caption来取数据,

双下划线的使用:只有通过values或者values_list这两种方式取数据的时候,后面才能用外键的__这种双下划线的方式取值,

注意:在编辑显示页面的时候,要进行{%for ...%}循环中,比如说要显示id号码,数据库的id可能出现断号的现象,所以为了显示出来保证依然是顺序的序列号可以传入参数形式像这样:<td {{forloop.parentcount/forloop.counter/forloop.count0/forloop.vercount/forloop.last/forloop.first}}>{{...}}<t/d>,其中.parentcount指代上一级循环的count值。

#注意:models.Tb1.objects.filter(name='seven').first()拿到的就不是QuerySet列表类型,而是第一个数据了

#注意:models.Tb1.objects.filter(name='seven').first()拿到的就不是QuerySet列表类型,而是第一个数据了

#注意:models.Tb1.objects.filter(name='seven').count()拿到有多少个

 

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

 

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

 

基本操作

Ajax操作弹出框实例:

 

 

1、data是服务器端返回的字符串,可以通过反序列化JSON.pares(data)变成字典

2、这里的返回方式一定要是:return.HttpResponse(''),不能用render和其他方式,因为前段要接受的是字符串,而不是网页,

3、这里的Ajax里面的数据都是一个一个拿的,还有一种方式可以将数据打包一起发给后台:data:$("#id").serialize(),这里的#id是form表单的id,因此,这种操作是把form表单里面的input内容数据进行打包,

建议:让服务器端永远返回一个字典:return.HttpResponse(json.dumps(字典))

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='seven').order_by('id') # asc
  • # models.Tb1.objects.filter(name='seven').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'))   #这里的values后面接了.,就不是只去id了,而是根据id分组的意思,分组完之后后面的就表示获取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、其他操作

extra

  • # extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
  • # Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
  • # Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
  • # Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
  • # Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

F

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


Q

  • # 方式一:
  • # Q(nid__gt=10)
  • # Q(nid=8) | Q(nid__gt=10)
  • # Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')

方式二:

  • # con = Q()
  • # q1 = Q()
  • # q1.connector = 'OR'
  • # q1.children.append(('id', 1))
  • # q1.children.append(('id', 10))
  • # q1.children.append(('id', 9))
  • # q2 = Q()
  • # q2.connector = 'OR'
  • # q2.children.append(('c1', 1))
  • # q2.children.append(('c1', 10))
  • # q2.children.append(('c1', 9))
  • # con.add(q1, 'AND')
  • # con.add(q2, 'AND')
  • # models.Tb1.objects.filter(con)


执行原生SQL

  • # from django.db import connection, connections
  • # cursor = connection.cursor() # cursor = connections['default'].cursor()
  • # cursor.execute("""SELECT * from auth_user where id = %s""", [1])
  • # row = cursor.fetchone()

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

利用双下划线和 _set 将表之间的操作连接起来

class UserProfile(models.Model):
  user_info = models.OneToOneField('UserInfo')
  username = models.CharField(max_length=64)
  password = models.CharField(max_length=64)

  def __unicode__(self):
    return self.username


class UserInfo(models.Model):
  user_type_choice = (
          (0, u'普通用户'),
          (1, u'高级用户'),
          )
  user_type = models.IntegerField(choices=user_type_choice)
  name = models.CharField(max_length=32)
  email = models.CharField(max_length=32)
  address = models.CharField(max_length=128)

  def __unicode__(self):
    return self.name


class UserGroup(models.Model):

  caption = models.CharField(max_length=64)

  user_info = models.ManyToManyField('UserInfo')

  def __unicode__(self):
    return self.caption


class Host(models.Model):
  hostname = models.CharField(max_length=64)
  ip = models.GenericIPAddressField()
  user_group = models.ForeignKey('UserGroup')

  def __unicode__(self):
    return self.hostname

表结构实例

user_info_obj = models.UserInfo.objects.filter(id=1).first()
print user_info_obj.user_type
print user_info_obj.get_user_type_display()
print user_info_obj.userprofile.password

user_info_obj = models.UserInfo.objects.filter(id=1).values('email', 'userprofile__username').first()
print user_info_obj.keys()
print user_info_obj.values()

一对一操作

类似一对一
1、搜索条件使用 __ 连接
2、获取值时使用 .    连接

多对多操作

  • user_info_obj = models.UserInfo.objects.get(name=u'武沛齐')
  • user_info_objs = models.UserInfo.objects.all()
  • group_obj = models.UserGroup.objects.get(caption='CEO')
  • group_objs = models.UserGroup.objects.all()


# 添加数据

  • #group_obj.user_info.add(user_info_obj)
  • #group_obj.user_info.add(*user_info_objs)


# 删除数据

  • #group_obj.user_info.remove(user_info_obj)
  • #group_obj.user_info.remove(*user_info_objs)


# 添加数据

  • #user_info_obj.usergroup_set.add(group_obj)
  • #user_info_obj.usergroup_set.add(*group_objs)


# 删除数据

  • #user_info_obj.usergroup_set.remove(group_obj)
  • #user_info_obj.usergroup_set.remove(*group_objs)


# 获取数据

  • #print group_obj.user_info.all()
  • #print group_obj.user_info.all().filter(id=1)


# 获取数据

  • #print user_info_obj.usergroup_set.all()
  • #print user_info_obj.usergroup_set.all().filter(caption='CEO')
  • #print user_info_obj.usergroup_set.all().filter(caption='DBA')

 

三、扩展:

a、自定义上传

def upload_file(request):
  if request.method == "POST":
    obj = request.FILES.get('fafafa')
    f = open(obj.name, 'wb')
    for chunk in obj.chunks():
    f.write(chunk)
    f.close()
  return render(request, 'file.html')

b、Form上传文件实例请参考Form模块讲解

tips:

1、当创建数据库数据的时候,如果create后面接一个字典作为参数,那么必须加上**,例如:dict={'name':'czk','pwd':'123'},modles.Test.objects.create(**dict);
2、通过类查询数据库信息的时候,返回的都是一个jqueryset类,并且,就算查到的数据只有一个,那么返回的也还是一个类,同时也是一个列表,所以记住,不管返回的数据多少个,都要通过循环查找具体数据,增删改查操作:create(),delete(),update(),filter();
3、当obj后面加上first(),这个参数后,就不在是一个列表了,就单单是一个类,娶不到就为None,还可以用count统计个数,如果>0就取到了,一般用户登陆的时候都是用first获取,能够拿到对应的所有用户数据;
4、在用模型修改数据表类型的时候,比如要增加一列,一定要在后面的参数后面加上null=Ture,然后执行makemigrations,如果要修改列,只要注销掉这一行,重复一次makemigrations就可以了,所以在修改数据库表的时候都要在manage.py下面先执行两条命令;
5、数据库表数据创建的时候,auto_now,auto_now_add这两个参数会自动帮你生成,create的时候不用加这个参数了,注意,auto_now_add更新时间只有在save()方式的时候才生效,在用create创建的时候是无效的;
6、数据库操作的时候使用choices,数据库存储元组的第一个值,最好是数字,而在用户看到的是一个选择项,就是第二个参数;
7、数据库操作的时候blank表示django操作是否为空,例如登陆的时候输入空会有错误提示,而不是数据库是否为空,verbose_name出纳室显示中文;
8、在数据库查询的时候如果经常要查的列可以设置索引,db_index=true;
9、数据库去数据的几种方式:all()取的是一个queryset对象类型,.values(‘字段名1’,‘字段2’),这种方式拿到的也是一个queryset,但是是经过筛选的,只列出参数后面的字段,以字典的形式出现,提取就和all一样,还有一种方式values_list(‘字段名1’,‘字段2’),这种方式获取的数据是元组组成的对象,而不是字典组成的对象了,因此提取的时候只能以下标索引取得数据了;
10、在跨表查询操作的时候,如果是用values的方式,跨表查必须用到双下划线__,而在all和filter的方式的时候,可以通过.跨表查询,这是因为values查询的是具体的字段,不是对象了,只能通过django自带的双下划线规则跨表查询,所以跨表的时候记住:values里面都是双下划线查询,values_list也是,其他all等都是.查询;
11、前端模板for循环的时候,如果要显示顺序序号,而没有断点,可以用for循环的forloop.counter顺序显示序号,forloop.counter0表示从0开始,forloop.revercounter表示反序列,forloop.first和forloop.last表示是否是第一个,最后一个循环;forloop.parentloop表示上一级的循环的6个loop循环属性分别给列出来,所以一般用counter来顺序显示需要展示的信息的序号;
12、当在前端添加或者删除数据的时候,后台视图views应该用redirect返回刷新后的页面,而不是render返回;
13、在使用ajax的时候,重新刷新页面:location.reload(),注意,后台最好用HttpResponse传递字符串,而且用json.dumps将字典转成字符串,因为ajax只能接受和传递序列化了的数据,前端可以通过JSON.pares将传递过来的字符串转换成字典,再进行操作;
14、mantomanyfield最多只能创建3列,而自定义一对多实现多对多的可以自定制多列;

15、联合索引遵循最左前缀,只能命中最左边的索引,单独查找后面的索引是命中不了查找内容的,主要用在用户名和密码索引,主要用户名就可以了,其实建立了索引就是在数据库又单独建立了一张表,使得查询更快速

16、model数据库文件操作:

现在,你只需要记住,改变模型需要这三步:

  • 编辑 models.py 文件,改变模型。
  • 运行 python manage.py makemigrations 为模型的改变生成迁移文件。
  • 运行 python manage.py migrate 来应用数据库迁移。

16、数据库修改被分解成生成和应用两个命令是为了让你能够在代码控制系统上提交迁移数据并使其能在多个应用里使用;这不仅仅会让开发更加简单,也给别的开发者和生产环境中的使用带来方便。默认生成的表:

涂了红色部分表时手动生成的两张表,

17、执行修改数据库操作的时候出现问题描述:django.db.utils.ProgrammingError: (1146, u"Table'' doesn't exist")

解决办法:在数据库里面删除所有表,在migrations文件夹里面把生成的数据表‘0001_initial’等删掉,重新执行 python manage.py makemigrations 和python manage.py migrate命令,

18、我们的 vote() 视图代码有一个小问题。代码首先从数据库中获取了 selected_choice 对象,接着计算 vote 的新值,最后把值存回数据库。如果网站有两个方可同时投票在 同一时间 ,可能会导致问题。同样的值,42,会被 votes 返回。然后,对于两个用户,新值43计算完毕,并被保存,但是期望值是44。

这个问题被称为竞争条件 。如果你对此有兴趣,你可以阅读官网相关模块来学习如何解决这个问题。

 

QuerySet操作:

一般在实际公司应用的时候,用硬盘换取速度,很少会遵守sql范式,一般都是一张表一个数据,这样查询就快了,因为链表查询比较慢

在进行数据库查询的时候可以添加如下参数:

 

 

当filter最后面,例如values('u_id').annotate(uid=Count('u_id')).filter(uid__gt>1),就相当于是执行了sql的having操作,相当于是根据什么做出统计的意思
distinct()去重,使用一般数据库的时候后面括号一般不能出现参数,除了postgreSql数据库;

 

 其他知识点参考官方文档:https://docs.djangoproject.com/zh-hans/2.0/topics/db/

 

Form验证

django中的Form一般有两种功能:

  • 生成html
  • 验证用户输入

应用:
1、新url提交表单的方式操作可以使用生成thml功能
2、当使用ajax的时候不使用生成HTML标签的功能,虽然都能用上,但是推荐只使用验证功能

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import re
from django import forms
from django.core.exceptions import ValidationError


def mobile_validate(value):
mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
if not mobile_re.match(value):
raise ValidationError('手机号码格式错误')


class PublishForm(forms.Form):

user_type_choice = (
(0, u'普通用户'),
(1, u'高级用户'),
)

user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice,
attrs={'class': "form-control"}))

title = forms.CharField(max_length=20,
min_length=5,
error_messages={'required': u'标题不能为空',
'min_length': u'标题最少为5个字符',
'max_length': u'标题最多为20个字符'},
widget=forms.TextInput(attrs={'class': "form-control",
'placeholder': u'标题5-20个字符'}))

memo = forms.CharField(required=False,
max_length=256,
widget=forms.widgets.Textarea(attrs={'class': "form-control no-radius", 'placeholder': u'详细描述', 'rows': 3}))

phone = forms.CharField(validators=[mobile_validate, ],
error_messages={'required': u'手机不能为空'},
widget=forms.TextInput(attrs={'class': "form-control",
'placeholder': u'手机号码'}))

email = forms.EmailField(required=False,
error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},
widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))

Form

def publish(request):
ret = {'status': False, 'data': '', 'error': '', 'summary': ''}
if request.method == 'POST':
request_form = PublishForm(request.POST)
if request_form.is_valid():
request_dict = request_form.clean()
print request_dict
ret['status'] = True
else:
error_msg = request_form.errors.as_json()
ret['error'] = json.loads(error_msg)
return HttpResponse(json.dumps(ret))

View

form用户注册和登陆:
1、在html页面生成登陆或者注册表单的时候,可以一行一行的写name或者pwd,也可以{{obj.as_p}}就会以<p>标签的形式列出来,也可以用ul或者lable等标签,但是自定制就不方便了,建议还是用第一种一行一行的写{{obj.user}}等;
2、form只做验证,不做渲染等其他操作,但是可以用widget小插件的形式将内容变成textare或者select等,而且还可以进行页面的渲染,比如;user=forms.CharField(
max_length=12,
widget=widget.Textarea(attr={'class':'c1'}),
)
3、今后尽量不要用forms.CharField()这种形式,而是用fields.CharField()这种形式比较好,

 Choice和select:

在使用form的功能的时候,其实就是调用了一个类的实例化,里面的字段都是静态字段,实例化之后,静态字段都调取处理方在内存了,当我们再对数据库进行增删改的操作的时候,实际上from并不能及时的更新这些信息,而还是从缓存中找之前存放的数据,为了避免出现在select功能中出现这种错误,我们应该在model创建数据类的时候,显示声明一个def __init__(self,*args,**kwargs),构造方法,将需要与数据库同步的数据连接到数据库指定字段,特别是在进行select的choice功能实现的时候,

当使用modelchoicefield方式的时候,那么在model创建数据表类的时候一定要带上def __str__(self): return ....这样的构造函数;

form中钩子的使用:

 

 通过ajax方式提交信息:

因为ajax只能传递和接受字符串信息,而form的error信息是一个类,因此,需要对其进行序列化,在前端进行反序列化,一种方式是后端进行两次序列化as_json,前端也进行两次pares,另一种方式就是通过在后端定义一个函数,通过as_data的方式,定义一个函数,将错误信息系列化,然后添加到要传递的信息字典的对应列

扩展:ModelForm

ModelForm

    a.class Meta:
         model=        # 对应Model的
         fields={'user','pwd',...}     # 字段
         exclude={...},    # 排除字段
         labels={'user':'用户','pwd':'密码',...},     # 提示信息
         help_texts={...},   # 帮助提示信息
         widgets={...},    # 自定义插件
         error_messages={...}# 自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS)
         field_classes=None     # 自定义字段类 (也可以自定义字段)改变数据库表字段在页面显示的时候不是实际类型,而是指定要输入的类型
         localized_fields=('birth_date',) # 本地化,如:根据不同时区显示数据
         如:
           数据库中
           2016-12-27 04:10:57
           settings中的配置
           TIME_ZONE = 'Asia/Shanghai'
           USE_TZ = True
           则显示:
           2016-12-27 12:10:57
    b. 验证执行过程
        is_valid -> full_clean -> 钩子 -> 整体错误
 
    c. 字典字段验证
        def clean_字段名(self):
            # 可以抛出异常
            # from django.core.exceptions import ValidationError
            return "新值"
    d. 用于验证
        model_form_obj = XXOOModelForm()
        model_form_obj.is_valid()
        model_form_obj.errors.as_json()
        model_form_obj.clean()
        model_form_obj.cleaned_data
    e. 用于创建
        model_form_obj = XXOOModelForm(request.POST)
        #### 页面显示,并提交 #####
        # 默认保存多对多
            obj = form.save(commit=True)
        # 不做任何操作,内部定义 save_m2m(用于保存多对多)
            obj = form.save(commit=False)
            obj.save()      # 保存单表信息
            obj.save_m2m()  # 保存关联多对多信息
 
    f. 用于更新和初始化
        obj = model.tb.objects.get(id=1)
        model_form_obj = XXOOModelForm(request.POST,instance=obj)
        ...
 
        PS: 单纯初始化
            model_form_obj = XXOOModelForm(initial={...})

在使用Model和Form时,都需要对字段进行定义并指定类型,通过ModelForm则可以省去From中字段的定义

class AdminModelForm(forms.ModelForm):

  class Meta:
    model = models.Admin    #依赖哪个数据表
    #fields = '__all__'    #显示数据库的全部字段
    fields = ('username', 'email')    #显示多少个字段

    widgets = {
    'email' : forms.PasswordInput(attrs={'class':"alex"}),
    }

ModelForm数据库验证知识.触发Model中的验证和错误提示有两种方式:

a. Django Admin中的错误信息会优先根据Admiin内部的ModelForm错误信息提示,如果都成功,才来检查Model的字段并显示指定错误信息
b. 调用Model对象的 clean_fields 方法,如:
# models.py
class UserInfo(models.Model):
  nid = models.AutoField(primary_key=True)
  username = models.CharField(max_length=32)

  email = models.EmailField(error_messages={'invalid': '格式错了.'})

# views.py
def index(request):
  obj = models.UserInfo(username='11234', email='uu')
  try:
    print(obj.clean_fields())
  except Exception as e:
    print(e)
  return HttpResponse('ok')

# Model的clean方法是一个钩子,可用于定制操作,如:上述的异常处理。

2.Admin中修改错误提示
# admin.py
from django.contrib import admin
from model_club import models
from django import forms


class UserInfoForm(forms.ModelForm):
  username = forms.CharField(error_messages={'required': '用户名不能为空.'})
  email = forms.EmailField(error_messages={'invalid': '邮箱格式错误.'})
  age = forms.IntegerField(initial=1, error_messages={'required': '请输入数值.', 'invalid': '年龄必须为数值.'})

  class Meta:
    model = models.UserInfo
    # fields = ('username',)
    fields = "__all__"


class UserInfoAdmin(admin.ModelAdmin):
  form = UserInfoForm

  admin.site.register(models.UserInfo, UserInfoAdmin)

 

 

更多Form内容参考网址:http://www.cnblogs.com/wupeiqi/articles/6144178.html

推荐阅读