分享

Python工程师详细讲解Django之model,弄懂了马上升职加薪(附源码)

 庆亮trj21bcn0z 2017-12-24


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中有四个类型:

Python工程师详细讲解Django之model,弄懂了马上升职加薪(附源码)

如果不做任何设置,添加用户时应该也能看到四个类型如下图:

Python工程师详细讲解Django之model,弄懂了马上升职加薪(附源码)

在代码中通过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},
)

Python工程师详细讲解Django之model,弄懂了马上升职加薪(附源码)

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(推荐这种方式)这个时候其实会生成四张表:

Python工程师详细讲解Django之model,弄懂了马上升职加薪(附源码)

可以通过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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899################################################################### PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET ################################################################### def all(self) # 获取所有的数据对象 def filter(self, *args, **kwargs) # 条件查询 # 条件可以是:参数,字典,Q def exclude(self, *args, **kwargs) # 条件查询,取反 # 条件可以是:参数,字典,Q def select_related(self, *fields) 性能相关:表之间进行join连表操作,一次性获取关联的数据。 model.tb.objects.all().select_related() model.tb.objects.all().select_related('外键字段') model.tb.objects.all().select_related('外键字段__外键字段') def prefetch_related(self, *lookups) 性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。 # 获取所有用户表 # 获取用户类型表where id in (用户表中的查到的所有用户ID) models.UserInfo.objects.prefetch_related('外键字段') from django.db.models import Count, Case, When, IntegerField Article.objects.annotate( numviews=Count(Case( When(readership__what_time__lt=treshold, then=1), output_field=CharField(), )) ) students = Student.objects.all().annotate(num_excused_absences=models.Sum( models.Case( models.When(absence__type='Excused', then=1), default=0, output_field=models.IntegerField() ))) def annotate(self, *args, **kwargs) # 用于实现聚合group by查询,这里的values就相当于分组 from django.db.models import Count, Avg, Max, Min, Sum v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')) # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id #这里最后面的filter这里就相当于sql语句的having条件 v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1) # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1 #distinct用于去重 v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1) # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1 def distinct(self, *field_names) # 用于distinct去重 models.UserInfo.objects.values('nid').distinct() # select distinct nid from userinfo 注:只有在PostgreSQL中才能使用distinct进行去重 def order_by(self, *field_names) # 用于排序 models.UserInfo.objects.all().order_by('-id','age') def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None) # 构造额外的查询条件或者映射,如:子查询 Entry.objects.extra(select={'new_id': 'select col from sometable where othercol > %s'}, select_params=(1,)) 换成原生的sql就是: select * (select col from sometable where othercool>1) as new_id from sometable Entry.objects.extra(where=['headline=%s'], params=['Lennon']) Entry.objects.extra(where=['foo='a' OR bar = 'a'', 'baz = 'a'']) Entry.objects.extra(select={'new_id': 'select id from tb where id > %s'}, select_params=(1,), order_by=['-nid']) def reverse(self): # 倒序 models.UserInfo.objects.all().order_by('-nid').reverse() # 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序,有order_by的时候吧reverse才会生效,单独使用reverse不生效 #defer和only可以根据自己的需要只取出自己想要的列的信息,obj = models.UserInfo.objects.defer('username','id'),如果这个时候obj.passwd也是可以获取数据的,但是django会再次进行一次查询数据库 def defer(self, *fields):#排除某列数据 models.UserInfo.objects.defer('username','id') 或 models.UserInfo.objects.filter(...).defer('username','id') def only(self, *fields): #仅取表中某几列的数据 models.UserInfo.objects.only('username','id') 或 models.UserInfo.objects.filter(...).only('username','id') def using(self, alias): 指定使用的数据库,参数为别名(setting中的设置)

关于Django ORM中效率的问题

select_related

models中的创建表的类:

123456789101112class UserType(models.Model): name = models.CharField(max_length=32) def __str__(self): return self.name 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函数中写如下代码:

123456def index(request): users_obj = models.User.objects.all() for row in users_obj: print(row.user,row.pwd,row.id,row.ut_id) print(row.ut.name) return HttpResponse('ok')

如果没有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次数据库查询,相对来说效率就降低很多

所以为了提高效率这里可以通过下面方式,将代码进行更改如下:

123456def index(request): users_obj = models.User.objects.all().select_related('ut') for row in users_obj: print(row.user,row.pwd,row.ut_id) print(row.ut.name) return HttpResponse('ok')

这里需要注意:select_related('ut')这里的参数只能是ForeignKey,也就是select_related实现了联表表查询

prefetch_related

代码如下:

123456def index(request): users_obj = models.User.objects..filter(id__gt=30).prefetch_related('ut') for row in users_obj: print(row.user,row.pwd,row.ut_id) print(row.ut.name) return HttpResponse('ok')

代码就是把原来的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里写如下代码:

123class UserInfo(models.Model): name = models.CharField(max_length=32) email = models.EmailField()

这样在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()

这样就会提示如下错误:

Python工程师详细讲解Django之model,弄懂了马上升职加薪(附源码)

查看full_clean源码

Python工程师详细讲解Django之model,弄懂了马上升职加薪(附源码)

可以看出clean_fields是对各个字段进行验证

下面还有一个clean方法,查看源码

Python工程师详细讲解Django之model,弄懂了马上升职加薪(附源码)

可以看出这里是Django预留的一个钩子

对于这个clean钩子的一个使用例子:

from django.core.exceptions import ValidationErrorclass UserInfo(models.Model): name = models.CharField(max_length=32) email = models.EmailField() def clean(self): c = UserInfo.objects.filter(name=self.name).count() if c: raise ValidationError(message='用户名已经存在',code='cc1')

这里通过clean定制了一个name值唯一的操作

这里需要知道full_clean验证的顺序:

1、验证每个字段的正则

2、验证clean钩子函数



以上是全部内容,只是善于分享,不足之处请包涵!爬虫基本的原理就是,获取源码,进而获取网页内容。一般来说,只要你给一个入口,通过分析,可以找到无限个其他相关的你需要的资源,进而进行爬取。


我也写了很多其他的非常简单的入门级的爬虫详细教程,关注后,点击我的头像,就可以查看到。


欢迎大家一起留言讨论和交流,谢谢!

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多