Django------model基础

模型:

模型是你的数据的唯一的、权威的信息源。

  • 每个模型都是django.db.models.Model 的一个Python 子类。
  • 模型的每个属性都表示为数据库中的一个字段。
  • Django 提供一套自动生成的用于数据库访问的API.

使用模型:appy应用后 manage.py makemigrations;manage.py migrate (表或表字段有更改时也需要做数据迁移)

字段:

字段类型:

模型中的每个字段都是 Field 子类的某个实例。( django.db.models.fields

字段类型

  • AutoField:一个根据实际ID自动增长的IntegerField,通常不指定
    • 如果不指定,一个主键字段将自动添加到模型中(用的时候为防止和自定义的ID冲突,可以用PK)
  • BooleanField:true/false 字段,此字段的默认表单控制是CheckboxInput
  • NullBooleanField:支持null、true、false三种值
  • CharField(max_length=字符长度):字符串,默认的表单样式是 TextInput
  • TextField:大文本字段,一般超过4000使用,默认的表单控件是Textarea
  • IntegerField:整数
  • DecimalField(max_digits=None, decimal_places=None):使用python的Decimal实例表示的十进制浮点数
    • DecimalField.max_digits:位数总数
    • DecimalField.decimal_places:小数点后的数字位数
  • FloatField:用Python的float实例来表示的浮点数
  • DateField[auto_now=False, auto_now_add=False]):使用Python的datetime.date实例表示的日期
    • 参数DateField.auto_now:每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为false
    • 参数DateField.auto_now_add:当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为false
    • 该字段默认对应的表单控件是一个TextInput. 在管理员站点添加了一个JavaScript写的日历控件,和一个“Today"的快捷按钮,包含了一个额外的invalid_date错误消息键
    • auto_now_add, auto_now, and default 这些设置是相互排斥的,他们之间的任何组合将会发生错误的结果
  • TimeField:使用Python的datetime.time实例表示的时间,参数同DateField
  • DateTimeField:使用Python的datetime.datetime实例表示的日期和时间,参数同DateField
  • FileField:一个上传文件的字段
  • ImageField:继承了FileField的所有属性和方法,但对上传的对象进行校验,确保它是个有效的image

字段选项:

  通过字段选项,可以实现对字段的约束,在字段对象时通过关键字参数指定

  • null:如果为True,Django 将空值以NULL 存储到数据库中,默认值是 False
  • blank:如果为True,则该字段允许为空白,默认值是 False(django admin填充数据时允许为空)
  • 对比:null是数据库范畴的概念,blank是表单验证证范畴的
  • db_column:字段的名称,如果未指定,则使用属性的名称
  • db_index:若值为 True, 则在表中会为此字段创建索引
  • default:默认值
  • primary_key:若为 True, 则该字段会成为模型的主键字段
  • unique:如果为 True, 这个字段在表中必须有唯一值
  • choices 它是一个可迭代的结构(比如,列表或是元组),由可迭代的二元组组成(比如[(A, B), (A, B)...]),用来给这个字段提供选择项。如果设置了 choices ,默认表格样式就会显示选择框,而不是标准的文本框,而且这个选择框的选项就是 choices 中的元组。每个元祖的第一个值做为select标签下option的value,第二个值为text显示文本。如果设置了choices,查询时想显示元祖第二哥元素时可用get_A_display()。
  • error_messages 参数能够让你重写默认抛出的错误信息。通过指定 key 来确认你要重写的错误信息。
  • help_text  文本将被显示在表单控件form中

关系

ORM:objects relationship map 对象关系映射

  表名(app名_表名)--------类名

  字段--------属性

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

关系的类型包括

  • ForeignKey:一对多,将字段定义在多的端中 
    • 若要创建一个递归的关联 —— 对象与自己具有多对一的关系 —— 请用models.ForeignKey('self')

    • 数据库中的表示:段名上添加"_id" 来创建数据库中的列名
    • 参数:
      • related_name:用于让关联的对象反查到源对
      • to_field:关联到的关联对象的字段名称
      • db_constraint:控制是否在数据库中为这个外键创建约束。默认值为True,如果被设置成False,访问一个不存在的关联对象将抛出 DoesNotExist 异常。
      • on_delete:当一个ForeignKey 引用的对象被删除时,Django 默认模拟SQL 的ON DELETE CASCADE 的约束行为,并且删除包含该ForeignKey的对象。 选项:CASCADE:级连删除,默认;PROTECT:抛出ProtectedError 以阻止被引用对象的删除。
  • ManyToManyField:多对多,将字段定义在两端中 
    • 建议:不建议从一个没有迁移的应用中创建一个ManyToManyField到一个具有迁移的应用
    • 数据库中的表示:默认情况下,会创建一张中间表,该表的名称使用多对多字段的名称和包含这张表的模型的名称生成,表名:app名_关联表1名_关联表2名,字段:自增主键,关联表1名_id,关联表2名_id
    • 参数:
      • related_name;
      • through:through 选项来指定自己手动创建的第三张表
      • through_field:指定表后可以指定字段
           members = models.ManyToManyField(Person, through='Membership', through_fields=('group', 'person'))
  • OneToOneField:一对一,将字段定义在任意一端中

选项:

使用内部的class Meta 定义模型的元数据,可用的参数:

  • ordering:对象默认的顺序,获取一个对象的列表时使用,符串前加-表示倒序,不加-表示正序
  • unique_togethe:联合主键;

  • verbose_name:对象的一个易于理解的名称,为单数;
    from django.db import models
    
    class Ox(models.Model):
        horn_length = models.IntegerField()
    
        class Meta:
            ordering = ['-order_date']
            unique_together = ("driver", "restaurant")
        
        
    示例

模型的方法:

  __str__():返回对象的字符串表达式(unicode格式

  get_absolute_url():计算一个对象的URL

管理器Manager:

  • 管理器是Django的模型进行数据库的查询操作的接口,Django应用的每个模型都拥有至少一个管理器objects
  • 自定义管理器类主要用于两种情况
    • 情况一:向管理器类中添加额外的方法;
      from django.db import models
      
      class PollManager(models.Manager):
          def with_counts(self):
              from django.db import connection
              cursor = connection.cursor()
              cursor.execute("""
                  SELECT p.id, p.question, p.poll_date, COUNT(*)
                  FROM polls_opinionpoll p, polls_response r
                  WHERE p.id = r.poll_id
                  GROUP BY p.id, p.question, p.poll_date
                  ORDER BY p.poll_date DESC""")
              result_list = []
              for row in cursor.fetchall():
                  p = self.model(id=row[0], question=row[1], poll_date=row[2])
                  p.num_responses = row[3]
                  result_list.append(p)
              return result_list
      
      class OpinionPoll(models.Model):
          question = models.CharField(max_length=200)
          poll_date = models.DateField()
          objects = PollManager()
      
      class Response(models.Model):
          poll = models.ForeignKey(OpinionPoll)
          person_name = models.CharField(max_length=50)
          response = models.TextField()
      示例
    • 情况二:修改管理器返回的原始查询集:重写get_queryset()方法
      # First, define the Manager subclass.
      class DahlBookManager(models.Manager):
          def get_queryset(self):
              return super(DahlBookManager, self).get_queryset().filter(author='Roald Dahl')
      
      # Then hook it into the Book model explicitly.
      class Book(models.Model):
          title = models.CharField(max_length=100)
          author = models.CharField(max_length=50)
      
          objects = models.Manager() # The default manager.
          dahl_objects = DahlBookManager() # The Dahl-specific manager.
      ##Book.objects.all()将返回数据库中所有的图书。而 Book.dahl_objects.all() 只返回作者#是 Roald Dahl 的图书。
      示例 

创建表对象:

  当创建对象时,django不会对数据库进行读写操作,调用save()方法才与数据库交互,将对象保存到数据库中

示例化方法一:

  • 模型类上添加一个类方法:
from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=100)

    @classmethod
    def create(cls, title):
        book = cls(title=title)
        # do something with the book
        return book

book = Book.create("Pride and Prejudice")
  • 在自定义管理器中添加一个方法(推荐)
    class BookManager(models.Manager):
        def create_book(self, title):
            book = self.create(title=title)
            # do something with the book
            return book
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
    
        objects = BookManager()
    
    book = Book.objects.create_book("Pride and Prejudice")
  • 根据类属性直接实例化和save方法,示例如下:

普通字段:

#方式1
publish_obj=Publish(name="人民出版社",city="北京",email="renMin@163.com")
publish_obj.save() # 将数据保存到数据库

#方式2    
#返回值publish_obj是添加的记录对象
publish_obj=Publish.objects.create(name="人民出版社",city="北京",email="renMin@163.com")

外键字段:

方式1:
   publish_obj=Publish.objects.get(nid=1)
   Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=665,pageNum=334,publish=publish_obj)

方式2:
   Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=665,pageNum=334,publish_id=1)

多对的字段:

book_obj=Book.objects.create(title="追风筝的人",publishDate="2012-11-12",price=69,pageNum=314,publish_id=1)
 
author_yuan=Author.objects.create(name="yuan",age=23,authorDetail_id=1)
author_egon=Author.objects.create(name="egon",age=32,authorDetail_id=2)
 
book_obj.authors.add(author_egon,author_yuan)    #  将某个特定的 model 对象添加到被关联对象集合中。   =======    book_obj.authors.add(*[])
 
book_obj.save()      

删除对象:

Book.objects.filter(id=1).delete()

#remove(obj1, obj2, ...)从关联的对象集中删除指定的模型对象。
#clear()从关联的对象集中删除所有的对象。

多对多关联的第三张表中的字段也一起删除了,默认的级联删除。

修改对象:

 author = models.Author.objects.filter(id=1)
    author.name = 'mona'
    author.save()

---------------- update方法直接设定对应属性----------------
    models.Book.objects.filter(id=3).update(title="PHP")
    ##sql:
    ##UPDATE "app01_book" SET "title" = 'PHP' WHERE "app01_book"."id" = 3; args=('PHP', 3)

查寻对象----查询集:

查询集表示从数据库中取出来的对象的集合,它可以含有零个、一个或者多个过滤器。

查询集是惰性执行的:创建查询集不会带来任何数据库的访问。

all(),获取一个表中所有对象

all_entries = Entry.objects.all()

filter(**kwargs)返回一个新的查询集,它包含满足查询参数的对象

Entry.objects.filter(pub_date__year=2006)
F对象:
>>> from django.db.models import F
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))
#添加  操作符
>>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))
#Q对象
list.filter(Q(pk_ _lt=6))
#字典;
filter可以引用的模型字段

链式过滤:

 Entry.objects.filter(
...     headline__startswith='What'
... ).exclude(
...     pub_date__gte=datetime.date.today()
... ).filter(
...     pub_date__gte=datetime(2005, 1, 30)
... )

get(),返回对象,没有报错

one_entry = Entry.objects.get(pk=1)

exclude:返回一个新的QuerySet,它包含不满足给定的查找参数的对象。

Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')

#等同于
SELECT ...
WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')

annotate,查询表达式可以是一个简单的值、模型(或关联模型)字段的一个引用或对查询集中的对象一个聚合函数(平均值、和等)。

>>> from django.db.models import Count
>>> q = Blog.objects.annotate(Count('entry'))
# The name of the first blog
>>> q[0].name
'Blogasaurus'

order_by,默认情况下,QuerySet 根据模型Meta 类的ordering 选项排序,你可以使用order_by 方法给每个QuerySet 指定特定的排序。

Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')

distinc,将去除查询结果中重复的行

values,返回一个QuerySet,每个字典表示一个对象,键对应于模型对象的属性名称。

# This list contains a dictionary.
>>> Blog.objects.filter(name__startswith='Beatles').values()
[{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]

values_list:迭代时返回的是元组,每个元组包含传递给values_list()调用的字段的值 ,第一个元素为第一个字段

Entry.objects.values_list('id', 'headline')
[(1, 'First entry'), ...]

datesdates(field, kind, order='ASC');field应为表中dateField的字段名。 kind应为"year""month""day"隐式的是升序排序

>>> Entry.objects.dates('pub_date', 'day', order='DESC')
[datetime.date(2005, 3, 20), datetime.date(2005, 2, 20)]

select_related返回一个QuerySet,当执行它的查询时它沿着外键关系查询关联的对象的数据。它会生成一个复杂的查询并引起性能的损耗,但是在以后使用外键关系时将不需要数据库查询。select_related限于单值关系 - 外键和一对一关系。

# Hits the database.
e = Entry.objects.select_related('blog').get(id=5)

# Doesn't hit the database, because e.blog has been prepopulated
# in the previous query.
b = e.blog

prefetch_related,返回QuerySet,它将在单个批处理中自动检索每个指定查找的相关对象。

from django.db import models

class Topping(models.Model):
    name = models.CharField(max_length=30)

class Pizza(models.Model):
    name = models.CharField(max_length=50)
    toppings = models.ManyToManyField(Topping)

    def __str__(self):              # __unicode__ on Python 2
        return "%s (%s)" % (self.name, ", ".join(topping.name
                                                 for topping in self.toppings.all()))


Pizza.objects.all().prefetch_related('toppings')

‘’’
Pizza .__ str __()要求self.toppings.all()它必须查询数据库,因此Pizza.objects .all()将在Pizza QuerySet中的每个项目的Toppings表上运行查询。
Pizza.objects.all().prefetch_related('toppings')
这意味着检索到的每个Pizza都会执行self.toppings.all();现在每次调用self.toppings.all(),而不是去数据库的项目,它会在预取的QuerySet缓存中找到它们填充在单个查询中。
‘’‘
示例

extra:extra() QuerySet 修改机制,它能在 QuerySet生成的SQL从句中注入新子句

extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)

Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})
#select语句
SELECT blog_entry.*, (pub_date > '2006-01-01') AS is_recent
FROM blog_entry;
Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
#select语句

SELECT * FROM blog_entry WHERE (foo='a' OR bar='a') AND (baz='a')

defer()具有延迟字段的查询集。

only()

Person.objects.defer("age", "biography")
Person.objects.only("name")

get_or_create 一个通过给出的kwargs 来查询对象的便捷方法(如果你的模型中的所有字段都有默认值,可以为空),需要的话创建一个对象。

try:
    obj = Person.objects.get(first_name='John', last_name='Lennon')
except Person.DoesNotExist:
    obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
    obj.save()


###############
obj, created = Person.objects.get_or_create(first_name='John', last_name='Lennon',
                  defaults={'birthday': date(1940, 10, 9)})

update_or_creat,通过给出的kwargs 来更新对象的便捷方法, 如果没有则创建一个新的对象

try:
    obj = Person.objects.get(first_name='John', last_name='Lennon')
    for key, value in updated_values.iteritems():
        setattr(obj, key, value)
    obj.save()
except Person.DoesNotExist:
    updated_values.update({'first_name': 'John', 'last_name': 'Lennon'})
    obj = Person(**updated_values)
    obj.save()

###########################
obj, created = Person.objects.update_or_create(
    first_name='John', last_name='Lennon', defaults=updated_values)

count()

Entry.objects.filter(headline__contains='Lennon').count()

first()&last():返回结果集的第一个对象, 当没有找到时返回None.

p = Article.objects.order_by('title', 'pub_date').first()

exists()如果QuerySet 包含任何结果,则返回True,否则返回False

if some_queryset.exists():
    print("There is at least one object in some_queryset")

#它快于下面这样的允许
if some_queryset:
    print("There is at least one object in some_queryset")

limit限制查询:

  • 查询集返回列表,可以使用下标的方式进行限制,等同于sql中的limit和offset子句
  • 注意:不支持负数索引
  • 使用下标后返回一个新的查询集,不会立即执行查询
  • 如果获取一个对象,直接使用[0],等同于[0:1].get(),但是如果没有数据,[0]引发IndexError异常,[0:1].get()引发DoesNotExist异常
Entry.objects.all()[5:10]

字段查找:(了不起的双下划线)

字段查询是指如何指定SQL WHERE子句的内容. 它们通过查询集filter()exclude() and get()的关键字参数指定.

exact:精确匹配,iexact:不区分大小写的精确匹配

Entry.objects.get(id__exact=14)

contains:区分大小写的包含例子。icontains:不区分大小写

Entry.objects.get(headline__contains='Lennon')

#sql语句
SELECT ... WHERE headline LIKE '%Lennon%'

in:在给定的列表。

Entry.objects.filter(id__in=[1, 3, 4])

#SELECT ... WHERE id IN (1, 3, 4);

gt(greater than):大于;gte:大于或等于;lt:小于;lte:小于等于

Entry.objects.filter(id__gt=4)
#SELECT ... WHERE id > 4;

startswith:区分大小写,以...开头;istartswith:不区分大小写

Entry.objects.filter(headline__istartswith='will')
#SELECT ... WHERE headline ILIKE 'Will%';

endswith:区分大小写,以...结束;iendswith:不区分大小写;

range:包含于之中

import datetime
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2005, 3, 31)
Entry.objects.filter(pub_date__range=(start_date, end_date))

SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';

year:对于日期和日期时间字段,确切的年匹配。整数年。

month:对于日期和日期时间字段,确切的月份匹配。取整数1(1月)至12(12月)。

day:对于日期和日期时间字段,具体到某一天的匹配。取一个整数的天数。

week_day:对于日期和日期时间字段,“星期几”匹配。取整数值,表示星期几从1(星期日)到7(星期六)。

hour:对于日期时间字段,精确的小时匹配。取0和23之间的整数。

Entry.objects.filter(pub_date__year=2005)
#SELECT ... WHERE pub_date BETWEEN '2005-01-01' AND '2005-12-31';
regex:正则表达式,区分大小写的正则表达式匹配。正则表达式语法是正在使用的数据库后端的语法。iregex:不区分大小写
Entry.objects.get(title__regex=r'^(An?|The) +')
#SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL

查寻相关的类:

Q()对象:Q() 对象 使得定义查询条件然后重用成为可能,使用| (OR) 和 &(AND) 操作符; 否则QuerySets中使用不了OR

#Q对象可以使用&(and)、|(or)操作符组合起来,当操作符应用在两个Q对象时,会产生一个新的Q对象
list.filter(pk_ _lt=6).filter(bcommet_ _gt=10)
list.filter(Q(pk_ _lt=6) | Q(bcommet_ _gt=10))

#使用~(not)操作符在Q对象前表示取反
list.filter(~Q(pk__lt=6))

#可以使用&|~结合括号进行分组,构造做生意复杂的Q对象
#过滤器函数可以传递一个或多个Q对象作为位置参数,如果有多个Q对象,这些参数的逻辑为and
#过滤器函数可以混合使用Q对象和关键字参数,所有参数都将and在一起,Q对象必须位于关键字参数的前面

F对象:可以使用模型的字段A与字段B进行比较,如果A写在了等号的左边,则B出现在等号的右边,需要通过F对象构造

list.filter(bread__gte=F('bcommet'))

 #F()对象中还可以写作“模型类__列名”进行关联查询

list.filter(isDelete=F('heroinfo__isDelete'))

 #对于date/time和数字字段进行运算

list.filter(bpub_date__lt=F('bpub_date') + timedelta(days=1))

基于对象的跨表查询 :

一对多:

  正向查询(按字段:publish):

book_obj=Book.objects.get(nid=1)
print(book_obj.publish) # book_obj.publish 是nid=1的书籍对象关联的出版社对象  

  反向查询(按表名:book_set)

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

一对一查询:

  正向查询

   author_egon=Author.objects.get(name="egon")
   print(author_egon.authorDetail.telephone)

  反向查询

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

多对多查询:

  正向查询(按字段:authors):

# 金瓶眉所有作者的名字以及手机号
    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)

基于双下划线的跨表查询:

   # 正向查询 按字段:publish

    queryResult=Book.objects.filter(publish__name="人民出版社")
            .values_list("title","price")

    # 反向查询 按表名:book

    queryResult=Publish.objects.filter(name="人民出版社")
              .values_list("book__title","book__price")
# 练习2: 查询egon出过的所有书籍的名字(多对多)

    # 正向查询 按字段:authors:
    queryResult=Book.objects
            .filter(authors__name="yuan")
            .values_list("title") # 反向查询 按表名:book queryResult=Author.objects
              .filter(name="yuan")
              .values_list("book__title","book__price")


# 练习3: 查询人民出版社出版过的所有书籍的名字以及作者的姓名 # 正向查询 queryResult=Book.objects
            .filter(publish__name="人民出版社")
            .values_list("title","authors__name") # 反向查询 queryResult=Publish.objects
              .filter(name="人民出版社")
              .values_list("book__title","book__authors__age","book__authors__name")

聚合查询:

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

>>> from django.db.models import Avg, Max, Min
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
#可以连表,可以反向
 Publisher.objects.aggregate(oldest_pubdate=Min('book__pubdate'))

聚合和其他查询集字句:

聚合也可以在过滤器中使用。 作用于普通模型字段的任何 filter()(或 exclude()) 都会对聚合涉及的对象进行限制。

使用annotate() 子句时,过滤器有限制注解对象的作用

>>> from django.db.models import Count, Avg
>>> Book.objects.filter(name__startswith="Django").annotate(num_authors=Count('authors'))

更多关于聚合的请参考:http://python.usyiyi.cn/documents/django_182/topics/db/aggregation.html

 

 

 

posted @ 2017-11-01 13:41  皖心  阅读(222)  评论(0编辑  收藏  举报