Django 模型层

一、简介

   模型层是对数据库的操作包括创建表查询等操作,其中运用的ORM

二、操作

  1、创建表

映射关系:

     表名  <-------> 类名

       字段  <-------> 属性

    表记录 <------->类实例对象

  所以根据想要的模型可以创建如下:

class Book(models.Model):
 
    nid = models.AutoField(primary_key=True)
    title = models.CharField( max_length=32)
    publishDate=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2)

  字段选项:

  对于字段的创建定义时的限制:

(1)null

如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.

(1)blank

如果为True,该字段允许不填。默认为False。
要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。

(2)default

字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用。

(3)primary_key

如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,
Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,
否则没必要设置任何一个字段的primary_key=True。

(4)unique

如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的

(5)choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices 中的选项。

这是一个关于 choices 列表的例子:

YEAR_IN_SCHOOL_CHOICES = (
    ('FR', 'Freshman'),
    ('SO', 'Sophomore'),
    ('JR', 'Junior'),
    ('SR', 'Senior'),
    ('GR', 'Graduate'),
)
每个元组中的第一个元素,是存储在数据库中的值;第二个元素是在管理界面或 ModelChoiceField 中用作显示的内容。 在一个给定的 model 类的实例中,想得到某个 choices 字段的显示值,就调用 get_FOO_display 方法(这里的 FOO 就是 choices 字段的名称 )。例如:

from django.db import models

class Person(models.Model):
    SHIRT_SIZES = (
        ('S', 'Small'),
        ('M', 'Medium'),
        ('L', 'Large'),
    )
    name = models.CharField(max_length=60)
    shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)


>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'

 常见Field Types

1、AutoField

如果没有指明主键,就会产生一个自增的主键。
 
2、BigIntegerField
64位的整型数值,从 -2^63 (-9223372036854775808) 到 2^63-1(9223372036854775807)。
 
3、BinaryField
存储原始二进制数据,仅支持字节分配。功能有限。
 
4、BooleanField
布尔型和NullBooleanField有区别,true/false,本类型不允许出现null。
 
5、CharField
字符串,一般都在创建时写入max_length参数。
 
6、CommaSeparatedIntegerField
逗号分隔的整数,考虑到数据库的移植性,max_length参数应该必选。
原文解释:A field of integers separated by commas. As in CharField, the max_length argument is required and the note about database portability mentioned there should be heeded.
 
7、DateField
时间,对应Python的datetime.date,额外的参数:DateField.auto_now表示是否每次修改时改变时间,DateField.auto_now_add 表示是否创建时自动设置时间,一般来说数据库重要的表都要有这样的字段记录创建字段时间和最后一次改变的时间。关于时间的话,建议timestamp,当然 python的话还是DateTime吧。
 
8、DateTimeField
对应Python的datetime.datetime,参照参数(7)。
 
9、DecimalField
固定精度的十进制数,一般用来存金额相关的数据。对应python的Decimal,额外的参数包括DecimalField.max_digits和DecimalField.decimal_places ,这个还是要参照一下mysql的Decimal类型
例如:price = models.DecimalField(max_digits=8,decimal_places=2)
 
10、EmailField
字符串,会检查是否是合法的email地址
 
11、FileField
class FileField([upload_to=None, max_length=100, **options])
存文件的,参数upload_to在1.7之前的一些老版本中必选的
 
12、FloatField
浮点数,必填参数:max_digits,数字长度;decimal_places,有效位数。
 
13、ImageField
class ImageField([upload_to=None, height_field=None, width_field=None, max_length=100, **options])
图片文件类型,继承了FileField的所有属性和方法。参数除upload_to外,还有height_field,width_field等属性。
 
14、IntegerField
[-2147483648,2147483647 ]的取值范围对Django所支持的数据库都是安全的。
 
15、IPAddressField
点分十进制表示的IP地址,如10.0.0.1
 
16、GenericIPAddressField
ip v4和ip v6地址表示,ipv6遵循RFC 4291section 2.2,
 
17、NullBooleanField
可以包含空值的布尔类型,相当于设置了null=True的BooleanField。
 
18、PositiveIntegerField
正整数或0类型,取值范围为[0 ,2147483647]
 
19、PositiveSmallIntegerField
正短整数或0类型,类似于PositiveIntegerField,取值范围依赖于数据库特性,[0 ,32767]的取值范围对Django所支持的数据库都是安全的。
 
20、SlugField
只能包含字母,数字,下划线和连字符的字符串,通常被用于URLs表示。可选参数max_length=50,prepopulate_from用于指示在admin表单中的可选值。db_index,默认为True。
 
21、SmallIntegerField
小整数字段,类似于IntegerField,取值范围依赖于数据库特性,[-32768 ,32767]的取值范围对Django所支持的数据库都是安全的。
 
22、TextField
文本类型
 
23、TimeField
时间,对应Python的datetime.time
 
24、URLField
存储URL的字符串,默认长度200;verify_exists(True),检查URL可用性。
 
25、FilePathField
class FilePathField(path=None[, match=None, recursive=False, max_length=100, **options])
类似于CharField,但是取值被限制为指定路径内的文件名,path参数是必选的。

通过migration和miagrate创建完成

  2、增加记录

方式1
publish_obj=Publish(name="人民出版社",city="北京",email="renMin@163.com")
publish_obj.save() # 将数据保存到数据库
1
2
方式2 <br>返回值publish_obj是添加的记录对象
publish_obj=Publish.objects.create(name="人民出版社",city="北京",email="renMin@163.com")

  3、查询

    1、通过所实例化的model进行用点来用

<1> all():                 查询所有结果
 
<2> filter(**kwargs):      它包含了与所给筛选条件相匹配的对象
 
<3> get(**kwargs):         返回与所给筛选条件相匹配的对象,返回结果有且只有一个,
                           如果符合筛选条件的对象超过一个或者没有都会抛出错误。
 
<5> exclude(**kwargs):     它包含了与所给筛选条件不匹配的对象
 
<4> values(*field):        返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列
                           model的实例化对象,而是一个可迭代的字典序列
 
<9> values_list(*field):   它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
 
<6> order_by(*field):      对查询结果排序
 
<7> reverse():             对查询结果反向排序
 
<8> distinct():            从返回结果中剔除重复纪录
 
<10> count():              返回数据库中匹配查询(QuerySet)的对象数量。
 
<11> first():              返回第一条记录
 
<12> last():               返回最后一条记录
 
<13> exists():             如果QuerySet包含数据,就返回True,否则返回False

    2、查询中的双下划线

models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值
 
models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in
 
models.Tb1.objects.filter(name__contains="ven")
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
 
models.Tb1.objects.filter(id__range=[1, 2])      # 范围bettwen and
 
startswith,istartswith, endswith, iendswith 

    3、跨表查询

      正向查询(按字段名查)

# 查询nid=1的书籍的出版社所在的城市<br>
book_obj=Book.objects.get(nid=1)<br>print(book_obj.publish.city) # book_obj.publish 是nid=1的书籍对象关联的出版社对象  

      反向查询

# 查询 人民出版社出版过的所有书籍
 
    publish=Publish.objects.get(name="人民出版社")
 
    book_list=publish.book_set.all()  # 与人民出版社关联的所有书籍对象集合
 
    for book_obj in book_list:
        print(book_obj.title)

    4、一对一查询

正向查询(按字段:authorDetail):
# 查询egon作者的手机号
 
    author_egon=Author.objects.get(name="egon")
    print(author_egon.authorDetail.telephone)

反向查询(按表名:author):

# 查询所有住址在北京的作者的姓名
 
    authorDetail_list=AuthorDetail.objects.filter(addr="beijing")
 
    for obj in authorDetail_list:
        print(obj.author.name)

    5、多对多查询 

正向查询
# 三国演义所有作者的名字以及手机号
 
    book_obj=Book.objects.filter(title="三国演义").first()
 
    authors=book_obj.authors.all()
 
    for author_obj in authors:
 
        print(author_obj.name,author_obj.authorDetail.telephone)
反向查询

# 查询egon出过的所有书籍的名字
 
    author_obj=Author.objects.get(name="egon")
    book_list=author_obj.book_set.all() #与egon作者相关的所有书籍
 
    for book_obj in book_list:
        print(book_obj.title)

注意:

你可以通过在 ForeignKey() 和ManyToManyField的定义中设置 related_name 的值来覆写 FOO_set 的名称。例如,如果 Article model 中做一下更改: publish = ForeignKey(Blog, related_name='bookList'),那么接下来就会如我们看到这般:

# 查询 人民出版社出版过的所有书籍
 
   publish=Publish.objects.get(name="人民出版社")
 
   book_list=publish.bookList.all()  # 与人民出版社关联的所有书籍对象集合

 

4、删除

删除方法就是 delete()。它运行时立即删除对象而不返回任何值。例如:

e.delete()

 

四、增加 ORM处理日志

可以通过logging配置看翻译成的sql语句

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}  

跨表查询:

#--------------------对象形式的查找--------------------------
    # 正向查找
    ret1=models.Book.objects.first()
    print(ret1.title)
    print(ret1.price)
    print(ret1.publisher)
    print(ret1.publisher.name)  #因为一对多的关系所以ret1.publisher是一个对象,而不是一个queryset集合

    # 反向查找
    ret2=models.Publish.objects.last()
    print(ret2.name)
    print(ret2.city)
    #如何拿到与它绑定的Book对象呢?
    print(ret2.book_set.all()) #ret2.book_set是一个queryset集合

#---------------了不起的双下划线(__)之单表条件查询----------------

#    models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值
#
#    models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
#    models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in
#
#    models.Tb1.objects.filter(name__contains="ven")
#    models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
#
#    models.Tb1.objects.filter(id__range=[1, 2])   # 范围bettwen and
#
#    startswith,istartswith, endswith, iendswith,

#----------------了不起的双下划线(__)之多表条件关联查询---------------

# 正向查找(条件)

#     ret3=models.Book.objects.filter(title='Python').values('id')
#     print(ret3)#[{'id': 1}]

      #正向查找(条件)之一对多

      ret4=models.Book.objects.filter(title='Python').values('publisher__city')
      print(ret4)  #[{'publisher__city': '北京'}]

      #正向查找(条件)之多对多
      ret5=models.Book.objects.filter(title='Python').values('author__name')
      print(ret5)
      ret6=models.Book.objects.filter(author__name="alex").values('title')
      print(ret6)

      #注意
      #正向查找的publisher__city或者author__name中的publisher,author是book表中绑定的字段
      #一对多和多对多在这里用法没区别

# 反向查找(条件)

    #反向查找之一对多:
    ret8=models.Publisher.objects.filter(book__title='Python').values('name')
    print(ret8)#[{'name': '人大出版社'}]  注意,book__title中的book就是Publisher的关联表名

    ret9=models.Publisher.objects.filter(book__title='Python').values('book__authors')
    print(ret9)#[{'book__authors': 1}, {'book__authors': 2}]

    #反向查找之多对多:
    ret10=models.Author.objects.filter(book__title='Python').values('name')
    print(ret10)#[{'name': 'alex'}, {'name': 'alvin'}]

    #注意
    #正向查找的book__title中的book是表名Book
    #一对多和多对多在这里用法没区别
View Code

 聚合查询和分组查询:

<1> aggregate(*args,**kwargs):

通过对QuerySet进行计算,返回一个聚合值的字典。aggregate()中每一个参数都指定一个包含在字典中的返回值。即在查询集上生成聚合。
from django.db.models import Avg,Min,Sum,Max

from django.db.models import Avg,Min,Sum,Max

从整个查询集生成统计值。比如,你想要计算所有在售书的平均价钱。Django的查询语法提供了一种方式描述所有
图书的集合。

>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}

aggregate()子句的参数描述了我们想要计算的聚合值,在这个例子中,是Book模型中price字段的平均值

aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的
标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定
一个名称,可以向聚合子句提供它:
>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}


如果你也想知道所有图书价格的最大值和最小值,可以这样查询:
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
View Code

<2> annotate(*args,**kwargs):

   可以通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和),即为查询集的每一项生成聚合。

# 统计每一本书的作者个数
# ret2=models.Book.objects.all().annotate(authors_num=Count("authors"))  # QuerySet
# print(ret2)  
# [book_obj1,book_obj2,book_obj3,book_obj4,....]
#
# for obj in ret2:
#     print(obj.nid,obj.title,obj.authors_num)
View Code

当些满足不了你的时候,这时候会用到F查询跟Q查询

F查询即把表字段放到相等的右边如:

查询书价大与自已书价3块钱的书
ret3=models.Publish.objects.filter(price__gt=F(price)+3)

Q为连联条件,当filter中一个条件不能满足你的时候,或想且,或的关系的时候可以用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、应用范围:

    # Each lookup function that takes keyword-arguments (e.g. filter(),
    #  exclude(), get()) can also be passed one or more Q objects as
    # positional (not-named) arguments. If you provide multiple Q object
    # arguments to a lookup function, the arguments will be “AND”ed
    # together. For example:

    Book.objects.get(
        Q(title__startswith='P'),
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
    )

    #sql:
    # SELECT * from polls WHERE question LIKE 'P%'
    #     AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')

    # import datetime
    # e=datetime.date(2005,5,6)  #2005-05-06

    # 5、Q对象可以与关键字参数查询一起使用,不过一定要把Q对象放在关键字参数查询的前面。
    # 正确:
    Book.objects.get(
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
        title__startswith='P')
    # 错误:
    Book.objects.get(
        question__startswith='P',
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))

 

传入条件进行查询:

q1 = Q()
q1.connector = 'OR'
q1.children.append(('id', 1))
q1.children.append(('id', 2))
q1.children.append(('id', 3))
    
models.Tb1.objects.filter(q1)

合并条件进行查询:

con = Q()

q1 = Q()
q1.connector = 'OR'
q1.children.append(('id', 1))
q1.children.append(('id', 2))
q1.children.append(('id', 3))

q2 = Q()
q2.connector = 'OR'
q2.children.append(('status', '在线'))

con.add(q1, 'AND')
con.add(q2, 'AND')

models.Tb1.objects.filter(con)

  

extra:

中可实现别名,条件,排序等,后面两个用 filter, exclude 一般都能实现,排序用 order_by 也能实现。我们主要看一下别名这个

比如 Author 中有 name, Tag 中有 name 我们想执行

SELECT name AS tag_name FROM blog_tag;

这样的语句,就可以用 select 来实现,如下:

In : tags = Tag.objects.all().extra(select={'tag_name': 'name'})

In : tags[0].name

Ou: 'Django'

In : tags[0].tag_name

我们发现 name 和 tag_name 都可以使用,确认一下执行的 SQL

In : Tag.objects.all().extra(select={'tag_name': 'name'}).query.__str__()

Out: u'SELECT (name) AS "tag_name", "blog_tag"."id", "blog_tag"."name" FROM "blog_tag"'

我们发现查询的时候弄了两次 (name) AS "tag_name" 和 "blog_tag"."name"

如果我们只想其中一个能用,可以用 defer 排除掉原来的 name (后面有讲)

In : Tag.objects.all().extra(select={'tag_name': 'name'}).defer('name').query.__str__()

Out]: u'SELECT (name) AS "tag_name", "blog_tag"."id" FROM "blog_tag"'

模型类的其他操作:

class User(models.Model):
    username = models.CharField(max_length=32,verbose_name="用户名")

    class Meta:
        verbose_name = "用户表"
        verbose_name_plural = "用户表"

# 所在app的名称 app01
User._meta.app_label
# verbose_name_plural,若没有设置,取 verbose_name
User._meta.verbose_name_plural
# verbose_name,若没有设置, 取 model_name
User._meta.verbose_name
# 模型名称 user
User._meta.model_name
# 通过字段名称获取字段对象, 获取 verbose_name 属性,若没有设置,取字段名称(username)
User._meta.get_field('username').verbose_name

 

from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation


class A(models.Model):
    """
    1 A1
    2 A2
    """
    name = models.CharField(max_length=64)
    c = GenericRelation(to='C')  # 关联查询


class B(models.Model):
    """
    1 B1
    2 B2
    """
    name = models.CharField(max_length=64)
    c = GenericRelation(to='C')  # 关联查询


class C(models.Model):
    """
    id  name   A    B    D    E   ... ...
    1   C1     1  null  null null
    2   C2     2  null  null null
    3   C3    null  1   null null
    4   C4    null  2   null null

    id  name   content_type   object_id
    1   C1      A               1
    2   C2      A               2
    3   C3      B               1
    4   C4      B               2
    """
    name = models.CharField(max_length=64)
    content_type = models.ForeignKey(to=ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')  # 关联查询

 

posted @ 2017-11-10 09:21  刘小伟  阅读(365)  评论(0编辑  收藏  举报