python能干的东西有很多,这里不再过多叙述,直接重点干货。 一大波model操作 model的功能:创建数据库表、操作数据库表、数据验证(弱) 关于自定义表名 下面是一个创建表的的类: 1 2 3 class User(models.Model): user = models.CharField(max_length = 32 ) pwd = models.CharField(max_length = 64 ) 这样默认生成的表名为app名称+下划线+类名这里即:app01_user 如果想要更改默认的表名,可以通过db_table参数实现,并且必须在 class Meta类下写,代码更改为更改为: 1 2 3 4 5 class User(models.Model): user = models.CharField(max_length = 32 ) pwd = models.CharField(max_length = 64 ) class Meta: db_table = 'user' 关于索引 按照之前的方式我们会这样创建表: 1 2 3 4 5 class User(models.Model): user = models.CharField(max_length = 32 ,db_index = True ) pwd = models.CharField(max_length = 64 ,db_index = True ) class Meta: db_table = 'user' 这样就会在本地生成两个文件来保存索引 联合索引 这里可以通过创建联合索引index_together从而生成一个文件,代码如下: 1 2 3 4 5 6 7 8 class User(models.Model): user = models.CharField(max_length = 32 ) pwd = models.CharField(max_length = 64 ) class Meta: db_table = 'user' index_together = [ ( 'user' , 'pwd' ) ] 联合索引的特点: 最左前缀 上面代码中生成索引是('user','pwd')是name在前面,所以这样会导致: select * from user where user = 'zhaofan' 可以命中索引 select * from user where user = 'zhaofan' and pwd = '123' 可以命中索引 select * from user where pwd = '123' 这种查询时则无法命中索引 所以联合索引的就会导致只有加上最左边的索引值时索引才能起作用,这里的name在最左边,所以查询时条件中有name时索引才能命中 联合唯一索引 unique_together = (('name', 'pwd'),) 表示('name','pwd')的组合是唯一的 多表操作及参数 一对多 下面是创建一对多的类: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class UserType(models.Model): name = models.CharField(max_length = 32 ) class User(models.Model): user = models.CharField(max_length = 32 ,db_index = True ) pwd = models.CharField(max_length = 64 ,db_index = True ) class Meta: db_table = 'user' index_together = [ ( 'name' , 'pwd' ) ] ut = models.ForeignKey( to = 'UserType' , to_field = 'id' , ) 如果数据库中已经有用户属于每个usertype,这样当我们通过下面方式删除usertype表中的数据时: UserType.objects.filter(id=1).delete() Django1.10之后不会报错,并且默认还会把属于usertype的用户user也删除 这里ForeignKey有几个参数: to 要进行关联的表名 to_field 要关联的表中的字段名称 on_delete 当删除关联表中的数据时,当前表与其关联的行的行为 - models.CASCADE,删除关联数据,与之关联也删除 - models.DO_NOTHING,删除关联数据,引发错误IntegrityError(这个错误是数据库提示的错误) - models.PROTECT,删除关联数据,引发错误ProtectedError(这个错误是Django提示的错误) - models.SET_NULL,删除关联数据,与之关联的值设置为null(前提该字段需要设置为可空) - models.SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提该字段需要设置默认值) - models.SET,删除关联数据 a. 与之关联的值设置为指定值,设置:models.SET(值) b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象即函数) 关于正向查找 model中的代码如下: 1 2 3 4 5 6 7 8 9 10 class UserType(models.Model): name = models.CharField(max_length = 32 ) class User(models.Model): user = models.CharField(max_length = 32 ) pwd = models.CharField(max_length = 64 ) ut = models.ForeignKey( to = 'UserType' , to_field = 'id' , ) 在views函数中写如下代码: 1 2 3 def index(request): v1 = models.User.objects. all ().values( 'user' , 'ut__name' ) return HttpResponse(v1) 即正常的通过models.User.objects.all().values('user','ut__name'),就可以实现跨表 当然也可以通过下面方式实现跨表查询: v2 = models.User.objects.all() for i in v2: print(i.ut.id,i.ut.name) 关于反向查找 通过v3 = models.UserType.objects.all().values('name','user__user')同样能够实现反向跨表查询 同样也可以通过for循环实现反向查询的跨表 v4 = models.UserType.objects.all() for i in v4: print(i.name,i.user_set.name) 这里的反向操作都需要用另外一张的表名_set__列名或者表名__列名 这里可以实现自定制related_name 在models里设置表结构的时候: 1 2 3 4 5 6 7 8 class User(models.Model): user = models.CharField(max_length = 32 ) pwd = models.CharField(max_length = 64 ) ut = models.ForeignKey( to = 'UserType' , to_field = 'id' , related_name = 'cc' ) 这样再次获取的时候就可以通过related_name值获取: v3 = models.UserType.objects.all().values('name','cc__user')这种方式和 3 = models.UserType.objects.all().values('name','user__user')结果是一样的 这里还有一个参数叫related_query_name 反向操作时,使用的连接前缀,用于替换“表名__set”中的“表名”,如果related_query_name=“aa”,则反向查找是可以通过aa_set查找 在Django admin中用到的参数 limit_choices_to 通过limit_choices_to,在Admin或ModelForm中显示关联数据时,提供的条件 如下图所示user type中有四个类型: 如果不做任何设置,添加用户时应该也能看到四个类型如下图: 在代码中通过limit_choices_to={'id__gt':2},可以修改显示的个数 1 2 3 4 5 6 7 8 9 class User(models.Model): user = models.CharField(max_length = 32 ) pwd = models.CharField(max_length = 64 ) ut = models.ForeignKey( to = 'UserType' , to_field = 'id' , related_name = 'cc' , limit_choices_to = { 'id__gt' : 2 }, ) db_constraint=True # 是否在数据库中创建外键约束 parent_link=False # 在Admin中是否显示关联数据 一对一OnetoOneField OneToOneField(ForeignKey) OneToOneField继承ForeignKey 所以同样有 to, # 要进行关联的表名 to_field=None # 要关联的表中的字段名称 on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为 当两个类之间有继承关系时,默认会创建一个一对一的字段 多对多ManyToManyField 常见的参数: to, # 要进行关联的表名 related_name # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all() related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名') limit_choices_to # 在Admin或ModelForm中显示关联数据时,提供的条件 特殊参数: symmetrical=None, 仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段 做如下操作时,不同的symmetrical会有不同的可选字段 symmetrical=True: class BB(models.Model): code = models.CharField(max_length=12) m1 = models.ManyToManyField('self',symmetrical=True) 这个时候可选字段有:code,id,m1 symmetrical=Falses时: class BB(models.Model): code = models.CharField(max_length=12) m1 = models.ManyToManyField('self',symmetrical=False) 这个时候可选字段有:code,id,m1,bb 其实这个时候发反向查询没有多大意义,毕竟这里是多丢多的自关联 创建对多的三种方式 第一种:让django自动创建第三张表,即通过models.ManyToManyField 1 2 3 4 5 class Blog(models.Model): site = models.CharField(max_length = 32 ) m = models.ManyToManyField( 'Tag' ) class Tag(models.Model): name = models.CharField(max_length = 32 ) 这样就可以通过 models.Blog.m.add models.Blog.m.update models.Blog.m.clear models.Blog.m.all models.Blog.m.create …… 第二种方式:手动创建第三张表 1 2 3 4 5 6 7 8 9 class Blog(models.Model): site = models.CharField(max_length = 32 ) class Tag(models.Model): name = models.CharField(max_length = 32 ) class B2T(models.Model): b = models.ForeignKey( 'Blog' ) t1 = models.ForeignKey( 'Tag' ) 操作第三张表的时候是通过B2T这个类可以直接操作 但是这个时候blog想要差tag的表的数据都必须借助于B2T表跨表到tag表,即不能直接跨表操作 第三种方式:手动创建第三张表并增加models.ManyToManyField(推荐这种方式)这个时候其实会生成四张表: 可以通过through以及through_fields从而让生成三张表而不是四张表 through表示和要和哪张表建立manytomany关系 through_fields表示要和第三表中那个字段建立关系 但是这个时候并不能通过models.Blog.m.add,以及update,remove,delete等,但是可以clear 并且这个时候不用在跨第三表去查询tag表。 ORM操作 基本操作增删查改 增 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() # 获取全部 models.Tb1.objects.filter(name='seven') # 获取指定条件的数据 models.Tb1.objects.exclude(name='seven') # 获取指定条件的数据,exclude表示非操作 删 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() 获取个数 models.Tb1.objects.filter(name='seven').count() 大于,小于 获取id大于1的值 models.Tb1.objects.filter(id__gt=1) 获取id大于等于1的值 models.Tb1.objects.filter(id__gte=1) 获取id小于10的值 models.Tb1.objects.filter(id__lt=10) 获取id小于10的值 models.Tb1.objects.filter(id__lte=10) 获取id大于1 且 小于10的值 models.Tb1.objects.filter(id__lt=10, id__gt=1) in 获取id等于11、22、33的数据 models.Tb1.objects.filter(id__in=[11, 22, 33]) not in models.Tb1.objects.exclude(id__in=[11, 22, 33]) isnull(只能通过这种表示空) Entry.objects.filter(pub_date__isnull=True) contains(相当于sql中的like) models.Tb1.objects.filter(name__contains='ven') models.Tb1.objects.filter(name__icontains='ven') icontains大小写不敏感 models.Tb1.objects.exclude(name__icontains='ven') range(相当于sql中的范围bettwen and) models.Tb1.objects.filter(id__range=[1, 2]) 范围bettwen and 其他类似 startswith,istartswith, endswith, iendswith, order by asc升序 models.Tb1.objects.filter(name='seven').order_by('id') desc降序 models.Tb1.objects.filter(name='seven').order_by('-id') group by from django.db.models import Count, Min, Max, Sum models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num')) 当后面annotate时,values表示根据哪一列分组 相当于下面的sql语句 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) 关于QuerySet方法详解 下面是Django的源码 第一部分获取的结果返回的都是QeurySet
关于Django ORM中效率的问题 select_related models中的创建表的类:
这样如果按照之前的方式在views函数中写如下代码:
如果没有print(row.ut.name)的时候,这个时候只有models.User.objects.all()这里进行了一次查表,如果user表里有10条数据,这样就仅仅进行一次查询,并且这个查询没有查到usertype表的数据。 如果有print(row.ut.name)的时候除了models.User.objects.all()这里进行了一次查表之外,每次row.ut.name的时候都要再次查询usertype表的数据,如果user表里有10条数据,就相当于一共执行了11次数据库查询,相对来说效率就降低很多 所以为了提高效率这里可以通过下面方式,将代码进行更改如下:
这里需要注意:select_related('ut')这里的参数只能是ForeignKey,也就是select_related实现了联表表查询 prefetch_related 代码如下:
代码就是把原来的select_related换成了prefetch_related,但是这个过程不同了 1、先执行select * from user where id>30 2、然后获取上一步骤中的获取的用户的ut_id,加入是1,2,3 3、最后执行select * from user_type where id in [1,2,3] 这样当后面通过for循环的获取值的的时候,其实数据已经被存在内存里,Django会自动去内存中寻找相应的数据,这样也能提高效率,减少查询数据的次数。 Django中的源码续 ################################################## # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS # ################################################## def raw(self, raw_query, params=None, translations=None, using=None): # 执行原生SQL models.UserInfo.objects.raw('select * from userinfo') # 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名 models.UserInfo.objects.raw('select id as nid from 其他表') # 为原生SQL设置参数 models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,]) # 将获取的到列名转换为指定列名 name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'} Person.objects.raw('SELECT * FROM some_other_table', translations=name_map) # 指定数据库 models.UserInfo.objects.raw('select * from userinfo', using='default') ################### 原生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() # fetchall()/fetchmany(..) def values(self, *fields): # 获取每行数据为字典格式 def values_list(self, *fields, **kwargs): # 获取每行数据为元祖 def dates(self, field_name, kind, order='ASC'): # 根据时间进行某一部分进行去重查找并截取指定内容 # kind只能是:'year'(年), 'month'(年-月), 'day'(年-月-日) # order只能是:'ASC' 'DESC' # 并获取转换后的时间 - year : 年-01-01 - month: 年-月-01 - day : 年-月-日 下面这句话就会截取到日,即年月日,如果填写的是year,则只会取到年 models.DatePlus.objects.dates('ctime','day','DESC') def datetimes(self, field_name, kind, order='ASC', tzinfo=None): # 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间 # kind只能是 'year', 'month', 'day', 'hour', 'minute', 'second' # order只能是:'ASC' 'DESC' # tzinfo时区对象 models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC) models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai')) ''' pip3 install pytz import pytz pytz.all_timezones pytz.timezone(‘Asia/Shanghai’) ''' def none(self): # 空QuerySet对象 #################################### # METHODS THAT DO DATABASE QUERIES # #################################### def aggregate(self, *args, **kwargs): # 聚合函数,获取字典类型聚合结果,对整个表做聚合 from django.db.models import Count, Avg, Max, Min, Sum result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid')) ===> {'k': 3, 'n': 4} result = models.UserInfo.objects.aggregate(n=count('nid'))相当于下面sql语句: select count(nid) as n from UserInfo 如果添加distinct=True则先去重在聚合 def count(self): # 获取个数 def get(self, *args, **kwargs): # 获取单个对象 def create(self, **kwargs): # 创建对象 def bulk_create(self, objs, batch_size=None): # 批量插入 # batch_size表示一次插入的个数 objs = [ models.DDD(name='r11'), models.DDD(name='r22') ] 参数10表示一次插入多少个,即将数据进行分批插入 models.DDD.objects.bulk_create(objs, 10) def get_or_create(self, defaults=None, **kwargs): # 如果存在,则获取,否则,创建 # defaults 指定创建时,其他字段的值,defaults后面的内容表示前面的username不存在的时候,用于创建 obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2}) def update_or_create(self, defaults=None, **kwargs): # 如果存在,则更新,否则,创建 # defaults 指定创建时或更新时的其他字段 obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1}) def first(self): # 获取第一个 def last(self): # 获取最后一个 def in_bulk(self, id_list=None): # 根据主键ID进行查找 id_list = [11,21,31] models.DDD.objects.in_bulk(id_list) def delete(self): # 删除 def update(self, **kwargs): # 更新 def exists(self): # 是否有结果 model里的验证功能(较弱) 当在models里写如下代码:
这样在views函数里用下面方式添加用户时: models.UserInfo.objects.create(name='root',email='123') 或者: obj = models.UserInfo(name='zhaofan',email = '123') obj.save() 这种情况下email的格式并不对,但是这样添加时是可以添加成功,并不会报错 但是通过obj = models.UserInfo(name='zhaofan',email = '123')方式添加用户时,obj这里有一个方法full_clean(),当添加这个方法的时候就会添加验证功能,完整代码如下: obj = models.UserInfo(name='zhaofan',email = '123') obj.full_clean() obj.save() 这样就会提示如下错误: 查看full_clean源码 可以看出clean_fields是对各个字段进行验证 下面还有一个clean方法,查看源码 可以看出这里是Django预留的一个钩子 对于这个clean钩子的一个使用例子:
这里通过clean定制了一个name值唯一的操作 这里需要知道full_clean验证的顺序: 1、验证每个字段的正则 2、验证clean钩子函数 以上是全部内容,只是善于分享,不足之处请包涵!爬虫基本的原理就是,获取源码,进而获取网页内容。一般来说,只要你给一个入口,通过分析,可以找到无限个其他相关的你需要的资源,进而进行爬取。 我也写了很多其他的非常简单的入门级的爬虫详细教程,关注后,点击我的头像,就可以查看到。 欢迎大家一起留言讨论和交流,谢谢! |
|
来自: 庆亮trj21bcn0z > 《编程》