Django基础
models
数据库ORM字段和参数
AutoField:
int 自增列,必须填入参数 primary_key=True
如果没有写 AutoField,则会自动创建一个列名为 id 的列
from django.db import models
class Person(models.Model):
id = models.AutoField(primary_key=True) # 自增的 id 主键
CharField:
字符类型,必须提供 max_length 参数, max_length 表示最大字符长度
from django.db import models
class Person(models.Model):
id = models.AutoField(primary_key=True) # 自增的 id 主键
name = models.CharField(max_length=32)
IntegerField:
整数类型,范围在 -2147483648 到 2147483647
from django.db import models
class Person(models.Model):
id = models.AutoField(primary_key=True) # 自增的 id 主键
name = models.CharField(max_length=32)
age = models.IntegerField()
DateField:
日期字段,日期格式 YYYY-MM-DD,相当于 Python 中的 datetime.date()
from django.db import models
class Person(models.Model):
id = models.AutoField(primary_key=True) # 自增的 id 主键
name = models.CharField(max_length=32)
age = models.IntegerField()
birthday = models.DateField(auto_now_add=True)
DatetimeField、DateField、TimeField 这三个时间字段,都可设置 auto_now_add、auto_now 属性
auto_now_add=True 的话,创建数据记录的时候会把当前时间赋给该字段
auto_now=True 的话,每次更新数据记录的时间会用来更新该字段
DateTimeField:
日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于 Python 中的 datetime.datetime()
BigAutoField(AutoField):
bigint 自增列,必须填入参数 primary_key=True
如果没有写自增列,则会自动创建一个列名为 id 的列
SmallIntegerField(IntegerField):
短整型,-32768 到 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField):
正短整型,0 到 32767
IntegerField(Field):
有符号的整数列,-2147483648 到 2147483647
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField):
正整数,0 到 2147483647
BigIntegerField(IntegerField):
有符号的长整型,-9223372036854775808 到 9223372036854775807
BooleanField(Field):
布尔值类型
NullBooleanField(Field):
可以为空的布尔值
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 格式的验证
TimeField(DateTimeCheckMixin, Field):
时间格式,HH:MM[:ss[.uuuuuu]]
DurationField(Field):
长整数,时间间隔,数据库中按照 bigint 存储,ORM 中获取的值为 datetime.timedelta 类型
FloatField(Field):
浮点型
DecimalField(Field):
10进制小数
参数:max_digits,小数总长度;decimal_places,小数位长度
BinaryField(Field):
二进制类型
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,上传图片的宽度保存的数据库字段名(字符串)
字段参数:
null
1、默认是False的,如果设置为True的时候,django将会映射到数据表指定是否为空
2、如果这个字段设置为False的时候,如果没给这个字段传递任何值的时候,django也会使用一个空字符串(’’)存储进去
3、如果这个字段设置为True的时候,django会产生两种空值的情形(null和空字符串)
4、如果想要在表单验证的时候允许这个字符串为空的时候,django建议使用blank=True
5、如果你的字段BooleanField的时候,可以为空的建议使用NullBooleanField
blank
1、这个字段是在表单验证的时候可以为空,默认是False
2、这个和null是有区别的
blank=True仅仅是在表单验证的时候可以为空
null=True仅仅是数据库级别的null
models类的继承
1、抽象基类
class Meta:
abstract = True
声明此类是抽象基类。在数据库中不会生成迁移文件。
2、多表继承
实例:
class Human(models.Model):
name = models.CharField(max_length=100, default="")
peiou = models.IntegerField(default=0)
class Baby(Human):
age = models.IntegerField(default=0)
desc = models.CharField(max_length=100, default="")
这种继承在数据中的字段表现:
baby表:
human表:
在看迁移文件:
migrations.CreateModel(
name='Baby',
fields=[
('human_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='cies_account.human')),
('age', models.IntegerField(default=0)),
],
bases=('cies_account.human',),
),
migrations.CreateModel(
name='Human',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
],
),
哇,不得了了,竟然是主动添加一对一关系
3、代理模式
有时候你可能并不想改变父类的字段内容,而仅仅是想改变模型的某些行为模式。这时候代理模式就是你的好选择
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class MyPerson(Person):
class Meta:
ordering = ["last_name"] #查找的时候以last_name排序
proxy = True
def do_something(self):
# ...
pass
>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>
具体应用场景 真没看明白
官方文档:django-models
4、模型管理器
模型管理器类(models.Manager)
实例:
# 自定义的模型管理类(继承models.Manager)
class BookInfoManager(models.Manager):
# 重写父类all()方法。 返回isDelete为False的所有记录。
def all(self):
# 默认返回所有记录。
# 调用父类的成员语法为:super().方法名
return super().all().filter(isDelete=False) # 通过过滤器,返回isDelete为False的所有记录。
# 封装创建模型类对象的方法
def create_book(self, title, pub_date):
# self.model可以获得self的模型类名(BookInfo)
book = self.model() # 创建模型类对象。
book.btitle = title
book.bpub_date = pub_date
book.bread = 0
book.bcommet = 0
book.isDelete = False
# 将数据插入进数据表
book.save()
return book
class BookInfo(models.Model):
btitle = models.CharField(max_length=20, db_column='title') # 图书名称
bpub_date = models.DateField() # 出版日期
bread = models.IntegerField(default=0) # 阅读量
bcomment = models.IntegerField(default=0) # 评论量
isDelete = models.BooleanField(default=False) # 删除标记
objects = BookInfoManager() # 使用自定义的管理器对象。
books = BookInfo.objects.all() # 调用的是自定义的管理器类的all()方法,返回isDelete为False的所有记录。
book = BookInfo.objects.create_book("abc", date(1980, 1, 1)) # 调用管理器封装的方法,创建模型类对象。
此时的iobjects就是你自定义的模型管理类。实际上就是一个继承重写的过程
CBV
class DTLogin(APIHandler):
def get(self):
return {}
@need_params("username", "password")
def post(self):
username = self.input.get("username")
password = self.input.get("password")
data = {"username": username, "password": password}
payloads = {
"iss": "ECORDIA",
"iat": time.time(),
}
user_token = token.token(data, payloads)
return {"Authorization": user_token}
middleware
一个request请求的行走流程,即生命周期如下:
所有的请求先走中间件的process_request的请求,最终走process_response返回资源给前端。
也就是说,每一个请求都是先通过中间件中的 process_request 函数,这个函数返回 None 或者 HttpResponse 对象,如果返回前者,继续处理其它中间件,如果返回一个 HttpResponse,就处理中止,返回到网页上。
示例:
直接上源码
class TokenMiddleware(MiddlewareMixin):
def process_request(self, request):
"""登陆验证"""
token = request.headers.get('Authorization', '') or request.COOKIES.get('passport')
user_dict = cache.get(token) if token else {}
if not user_dict:
try:
url_pattern = r"^/api((%s))/?$" % ")|(".join([
"/account/create/super",
"/account/login",
"/account/online/ws",
"/account/login/auth",
"/account/company/apply",
"/device/sync/server-dt",
"/device/sync/server-dt-off",
"/report/sync/server-report-dt",
"/report/detail/get_report_detail",
"/report/info/down_report_dt",
"/record/sync/server-dt-record",
# "/record/detail/get_charts_detail",
# "/record/all/get_task_record",
# "/report/delete",
])
if not re.match(url_pattern, request.path):
raise AuthError("认证失败!")
except AuthError as e:
return KPResponse({'code': e.code, 'data': None, 'msg': e.msg, 'cost': '0ms'},
content_type='application/json')
elif user_dict and re.match(r"^/api/account/login/?$", request.path):
return KPResponse({'code': 0, 'data': user_dict, 'msg': "success", 'cost': '1ms'},
content_type='application/json')
elif user_dict.get("username", "ecordia") == "ecordia" and request.method.lower() == "post":
# raise AuthError("认证失败!")
return KPResponse({'code': 2, 'data': None, 'msg': "无权限操作!"}, content_type='application/json')
request.user = user_dict
def process_response(self, request, response):
# response['Cache-Control'] = 'no-cache'
# response["Access-Control-Allow-Origin"] = "*"
# response["Access-Control-Allow-Methods"] = "POST, GET, HEAD, OPTIONS"
# response["Access-Control-Max-Age"] = "1000"
# response["Access-Control-Allow-Headers"] = "*"
return response
我们在网站放到服务器上正式运行后,DEBUG改为了 False,这样更安全,但是有时候发生错误我们不能看到错误详情,调试不方便,有没有办法处理好这两个事情呢?
普通访问者看到的是友好的报错信息
管理员看到的是错误详情,以便于修复 BUG
class UserBasedExceptionMiddleware(object):
def process_exception(self, request, exception):
if request.user.is_superuser or request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS:
return technical_500_response(request, *sys.exc_info())
CRUD
查询数据
#all() 查询所有结果
book_list=Book.objects.all() #<QuerySet [<Book: book1>, <Book: book2>, <Book: book3>]>
#filter()它包含了与所给筛选条件相匹配的对象
book_list = Book.objects.filter(id=1) #<QuerySet [<Book: book1>]>
#get() model对象 有且只有一个查询结果时才有意义 如果超过一个或者没有都会抛出异常
book = Book.objects.get(id=2) #<Book: book2>
#order_by() model对象 对查询结果排序
book = Book.objects.all().order_by("-id") #<QuerySet [<Book: book3>, <Book: book2>, <Book: book1>]>
#reverse() 对查询结果反向排序
book = Book.objects.all().order_by("-id").reverse()
#count() 返回匹配查询对象的数量
book = Book.objects.all().order_by("-id").count() #3
#exists() 如果Queryset包含数据,则返回true,否则返回false
book = Book.objects.all().exists() #True
book = Book.objects.filter(id=20).exists() #False
#values() 返回一个valueQureyset 是一个可迭代的字典序列
book = Book.objects.all().values("title") #<QuerySet [{'title': 'book1'}, {'title': 'book2'}, {'title': 'book3'} ]>
#values_list() 返回的是一个元组序列,values返回的是一个字典序列
book = Book.objects.all().values_list("title")#<QuerySet [('book1',), ('book2',), ('book3',)]>
#distinct() 从返回结果中剔除重复记录
book = Book.objects.all().distinct()
#first() 返回第一条记录
book = Book.objects.all().first() #<Book: book1>
#last() 返回最后一条记录
book = Book.objects.all().last() #<Book: book6>
#exclude() 它包含了与所给筛选条件不匹配的对象
book = Book.objects.all().exclude(id=2) #<QuerySet [<Book: book1>, <Book: book3>, <Book: book4>]>
将查询出的queryset对象序列化成json
objectQuerySet = ConventionCard.objects.filter(ownerUser = user)
data = serializers.serialize('json', list(objectQuerySet), fields=('fileName','id'))
双下划线查询
#id 大于3且小于6的值
book = Book.objects.filter(id__lt=6,id__gt=3) #<QuerySet [<Book: book4>, <Book: book5>]>
#获取id等于1、2、3的数据
book = Book.objects.filter(id__in=[1,2,3]) #<QuerySet [<Book: book1>, <Book: book2>, <Book: book3>]>
# not in
book = Book.objects.filter().exclude(id__in=[1,2,3]) #<QuerySet [<Book: book4>, <Book: book5>, <Book: book6>]>
#icontains大小写不敏感
book = Book.objects.filter(title__contains="book")
book = Book.objects.filter(title__icontains="book")
# 范围bettwen and
book = Book.objects.filter(id__range=[1,4])
正向查询和反向查询
# 表结构
class Question(models.Model):
question_text = models.CharField(max_length=200, verbose_name='话题名称')
author = models.ForeignKey(User, default=1, on_delete=models.CASCADE, verbose_name='创建人')
pub_date = models.DateTimeField(auto_now_add=True, verbose_name='创建日期')
picture = models.FileField(blank=True, null=True, verbose_name='图片')
def __str__(self):
return self.question_text
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
class Meta:
verbose_name = "话题"
verbose_name_plural = verbose_name
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE, verbose_name='话题名称')
choice_text = models.CharField(max_length=200, verbose_name='评论内容')
author = models.ForeignKey(User, default=1, on_delete=models.CASCADE, verbose_name='评论人')
picture = models.FileField(blank=True, null=True, verbose_name='图片')
def __str__(self):
return self.choice_text
class Meta:
verbose_name = "评论"
verbose_name_plural = verbose_name
# --------------------------------------------------------------------------------------------------
# 正向查询 Question-说说表 Choice-评论表 带有外键关联Question表 即Question-B表 Choice-A表
# 就是在查询A表的数据时,A表中有关联了B表的外键,这样就可以通过A表的外键字段关联到B表,从而通过B表中数据的条件来限制A表查询出的数据
# 查询评论内容为nero的话题 A->B -->正向查询
res = Choice.objects.get(choice_text="nero").question.question_text
# 反向查询
# 就是需要通过B表查询A表的数据,但是A表中没有关联B表的外键时的查询情况
# 查询评论内容为nero的话题
res = Question.objects.get(choice__choice_text="nero")
# 查询话题为’dota2 hero’的所有评论
# 正向查询
ch = Choice.objects.filter(question__question_text='dota2 hero').values_list('choice_text', flat=True)
# 反向查询
qs = Question.objects.get(question_text='dota2 hero')
qs.choice_set.all().values_list('choice_text', flat=True)
"""
正向查询是根据外键的 字段进行关联查询的
反向查询是根据表名小写或者表名小写加_set来查询的,至于用哪种方法,得看是哪种情况了
"""
聚合和分组查询
聚合查询
聚合函数
from django.db.models import Count, Sum, Max, Min, Avg
Count:统计
Sum:求和
Max:最大值
Min:最小值
Avg:平均值
from django.db.models import Avg
Book.objects.all().aggregate(Avg('price')) #{'price__avg': 107.14285714285714}
aggregate()是QuerySet的一个终止子句,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想为聚合值指定一个名称,可以向聚合子句提供它
多聚合函数聚合查询
Book.objects.aggregate(Avg('price'),Max('price'),Min('price'))#{'price__avg': 107.14285714285714, 'price__max': Decimal('200.00'), 'price__min': Decimal('30.00')}
分组
# 查询每一个出版社的名称以及出版过的书籍个数
queryset_result = Publish.objects.all().annotate(c=Count("book")).values("name","c")
print(queryset_result)
# 查询每一个作者的名字以及出版过的所有书籍的最高价格
queryset_result = Author.objects.all().annotate(max_Price=Max("book__price")).values("name","max_Price")
print(queryset_result)
# 查询每一本书的名字,对应出版社名称以及作者的个数
queryset_result = Book.objects.all().annotate(c=Count("authors")).values("title","publish__name","c")
print(queryset_result)
#根据一本图书作者数量的多少对查询集 QuerySet进行排序:
Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')
F和Q查询
如果我们要对两个字段的值做比较的话可以使用F查询,F()的实例可以在查询中引用字段,来比较同一个model实例中两个不同字段的值
# 查询评论数大于收藏数的书籍
from django.db.models import F
Book.objects.filter(commnetNum__lt=F('keepNum'))
Django支持F()对象以及F()对象和常数之间的加减乘除和取模的操作
# 查询评论数大于收藏数2倍的书籍
Book.objects.filter(commnetNum__lt=F('keepNum')*2)
修改操作也可以使用F函数
#将每一本书的价格提高30元
Book.objects.all().update(price=F("price")+30)
filter()等方法中的关键字参数查询都是一起进行“AND”的,如果你需要执行更复杂的查询(例如or语句),你可以使用Q对象
Q对象可以使用&和|操作符组合起来,当一个操作符在两个Q对象上使用时,它产生一个新的Q对象
bookList=Book.objects.filter(Q(authors__name="a1")|Q(authors__name="a2"))
Q对象可以使用~操作符取反
bookList=Book.objects.filter(Q(authors__name="a1") & ~Q(publishDate__year=2017)).values_list("title")
查询函数可以混合使用Q对象和关键字参数,所有提供给查询函数的参数(关键字参数或对象)都将“AND”在一起,但是如果出现Q对象,它必须位于所有关键字参数的前面
bookList=Book.objects.filter(Q(publishDate__year=2016)|Q(publishDate__year=2017),title__icontains="python" )
代码示例:
def get(self):
data = self.input
title = list(eval(data.get("title")))
author = list(eval(data.get("author")))
# Q查询实现where子句的or查询
res = Article.objects.filter(Q(title__in=title) | Q(author__in=author)).values(*["title", "classify", "author", "desc", "contents", "id"])
"""
对应的sql语句
SELECT * FROM `mi_article` WHERE (`mi_article`.`title` IN (阿飞正传, 飞虎外传)
OR `mi_article`.`author` IN (阿飞, 飞虎))
"""
res = Article.objects.filter(Q(title__in=title) | Q(author__in=author), contents__exact=66666666).values(*["title", "classify", "author", "desc", "contents", "id"])
"""
对应的sql语句
SELECT * FROM `mi_article` WHERE ((`mi_article`.`title` IN (阿飞正传, 飞虎外传)
OR `mi_article`.`author` IN (阿飞, 飞虎)) AND `mi_article`.`contents` = 66666666)
"""
"""
从以上总结出来,filter实现不了or连接,只能实现and,子句里都是and
"""
# F查询实例 查出成本比卖价高的文章
res = Article.objects.filter(cost__gte=F("price")).values()
# F查询查出成本比卖价+5还高的文章
res = Article.objects.filter(cost__gte=F("price")+5).values()
"""
对应的sql:
SELECT * FROM `mi_article` WHERE `mi_article`.`cost` >= `mi_article`.`price`
SELECT * FROM `mi_article` WHERE `mi_article`.`cost` >= (`mi_article`.`price` + 5)
"""
# F给某个字段做统一操作
res = Article.objects.update(price=F("price")+5) # 返回修改的数量
# exclude实现查询不查询某一个或者一组数据
res = Article.objects.filter(id__in=[103, 104, 105]).exclude(id__in=[103,104])
res = Article.objects.all().exclude(id=103)
"""
sql语句:
SELECT *` FROM `mi_article` WHERE (`mi_article`.`id` IN (103, 104, 105)
AND NOT (`mi_article`.`id` IN (103, 104)))
"""
# order_by排序,前面加个 - 表示倒序
res = Article.objects.filter().order_by("-id")
res = Article.objects.filter().order_by("-id").reverse()
"""
对应的sql:
SELECT * FROM `mi_article` ORDER BY `mi_article`.`id` DESC
结果:[<Article: Article object (108)>, <Article: Article object (107)>,
<Article: Article object (106)>, <Article: Article object (105)>,
<Article: Article object (104)>, <Article: Article object (103)>]
SELECT * FROM `mi_article` ORDER BY `mi_article`.`id` ASC
结果:[<Article: Article object (103)>, <Article: Article object (104)>,
<Article: Article object (105)>, <Article: Article object (106)>,
<Article: Article object (107)>, <Article: Article object (108)>]>
"""
# Concat对字符串操作,Value表示要拼接什么
res = Article.objects.update(title=Concat(("title"), Value("秀儿"))) # 返回值是修改的数量
# 查出卖价比成本+10还高的文章,标题标记 大热
res = Article.objects.filter(price__gte=F("cost") + 5).update(title=Concat(("title"), Value("大热")))# 返回值是修改的数量
# 双下划线查询
# exact 模糊查询
res = Article.objects.filter(contents__exact="66666666") # where子句=查询
res = Article.objects.filter(author__iexact="mmp") #where子句like查询, 不区分大小写
"""
sql:
SELECT * FROM `mi_article` WHERE `mi_article`.`contents` = 66666666
SELECT * FROM `mi_article` WHERE `mi_article`.`contents` LIKE 66666666
"""
# contains 包含查询
res = Article.objects.filter(desc__contains="猛") # 大小写敏感
res = Article.objects.filter(desc__icontains="MMP")# 大小写不敏感
"""
sql:
SELECT * FROM `mi_article` WHERE `mi_article`.`desc` LIKE BINARY %m%
SELECT * FROM `mi_article` WHERE `mi_article`.`desc` LIKE %MMP%
"""
增加数据
models.UserInfo.objects.create(user='yangmv',pwd='123456')
或者
obj = models.UserInfo(user='yangmv',pwd='123456')
obj.save()
或者
dic = {'user':'yangmv','pwd':'123456'}
models.UserInfo.objects.create(**dic)
批量创建数据
def post(self, request):
try:
data = json.loads(request.body)
# 批量存入数据库
li = []
start_time = time.time()
for i in data:
li.append(
Article(
title=i.get("title"),
classify=i.get("classify"),
author=i.get("author"),
desc=i.get("desc"),
contents=i.get("contents"),
)
)
# Article.objects.create(**i)
Article.objects.bulk_create(li) # 主要执行函数
print("cost:", time.time()-start_time)
# 单个存入数据库
except Exception as e:
print(e)
return HttpResponse("error insert")
return HttpResponse(json.dumps({"code": "success"}))
删除数据
models.UserInfo.objects.filter(user='yangmv').delete()
修改数据
models.UserInfo.objects.filter(user='yangmv').update(pwd='520')
或者
obj = models.UserInfo.objects.get(user='yangmv')
obj.pwd = '520'
obj.save()