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)) #字典;
链式过滤:
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