目录
2.1 model中的manager从哪儿来--ModelBase
2.4 以models.objects.first()为例分析
3.2.3 filter(**kwargs)、exclude(**kwargs)、get(**kwargs)
values()和values_list()——对应select 某几个字段
prefetch_related(*field) ——对应返回关联记录实体的集合
aggregate(*args, **kwargs)——对应聚合函数
contains/startswith/endswith——对应like
exclude(field__in=iterable)——对应not in
Django - ORM分析
一 Django ORM
Django ORM用到三个类:Manager、QuerySet、Model。
Manager 定义表级方法(表级方法就是影响一条或多条记录的方法),我们可以以models.Manager为父类,定义自己的 manager,增加表级方法;
QuerySet:Manager类的一些方法会返回QuerySet实例,QuerySet是一个可遍历结构,包含一个或多个元素,每个元素都是一个Model 实例,它里面的方法也是表级方法,前面说了,Django给我们提供了增加表级方法的途径,那就是自定义manager类,而不是自定义QuerySet类,一般的我们没有自定义QuerySet类的必要;
django.db.models模块中的Model类,我们定义表的model时,就是继承它,它的功能很强大,通过自定义model的instance可以获取外键实体等,它的方法都是记录级方法(都是实例方法,貌似无类方法),不要在里面定义类方法,比如计算记录的总数,查看所有记录,这些应该放在自定义的manager类中。以Django1.6为基础。
每个Model都有一个默认的manager实例,名为objects。
二 Manager分析
在django中常常见到 model.objects.using(db).all() 或者 model.objects.get(pk=xx), 又或者 model.objects.first()等等关于对数据库的操作,下面以mysql为代表,简要分析一下上面的种种操作,背后到底做了什么?(先重点提示一下,关注_fetch_all函数)
2.1 model中的manager从哪儿来--ModelBase
首先,model.objects中的 objects 是 model 的 manager,manager 是 Manager 类,俗称管理器。他是继承了一个以 BaseManager 为基础的类,这个类通过BaseManager.get_queryset(QuerySet),将QuerySet类的所有方法都添加到了这个manager中,所以model.objects.using(db).all()其实就是model.objects.queryset.using(db).all();model.objects.get(pk=xx)就等于model.objects.queryset.get()。
那么每一个model中的manager从哪儿来的呢?见ModelBase中的__new__()方法最后调用的_prepare()方法,_prepare方法最后创建Manager属性,_prepare的源代码如下:
def_prepare(cls):"""
Creates some methods once self._meta has been populated.
"""opts = cls._meta opts._prepare(cls)ifopts.order_with_respect_to: cls.get_next_in_order = curry(cls._get_next_or_previous_in_order, is_next=True) cls.get_previous_in_order = curry(cls._get_next_or_previous_in_order, is_next=False)# Defer creating accessors on the foreign class until it has been# created and registered. If remote_field is None, we're ordering# with respect to a GenericForeignKey and don't know what the# foreign class is - we'll add those accessors later in# contribute_to_class().ifopts.order_with_respect_to.remote_field: wrt = opts.order_with_respect_to remote = wrt.remote_field.model lazy_related_operation(make_foreign_order_accessors, cls, remote)# Give the class a docstring -- its definition.ifcls.__doc__isNone: cls.__doc__ ="%s(%s)"% (cls.__name__,", ".join(f.nameforfinopts.fields)) get_absolute_url_override = settings.ABSOLUTE_URL_OVERRIDES.get(opts.label_lower)ifget_absolute_url_override:setattr(cls,'get_absolute_url', get_absolute_url_override)ifnotopts.managersorcls._requires_legacy_default_manager():ifany(f.name =='objects'forfinopts.fields):raiseValueError("Model %s must specify a custom Manager, because it has a ""field named 'objects'."% cls.__name__ ) manager = Manager()# 重点关注这里manager.auto_created =Truecls.add_to_class('objects', manager) signals.class_prepared.send(sender=cls)
所以是每一个model类,都有自己的Manager。
2.2 Manager
而对Manager类,从源码django/db/models/manager.py中可以得到:
classManager(BaseManager.from_queryset(QuerySet)):pass
Manager是一个继承自BaseManager.from_queryset(QuerySet)类的类,到底是一个怎样的呢?同样在django/db/models/manager.py中可以找到答案
@classmethoddeffrom_queryset(cls, queryset_class, class_name=None):ifclass_nameisNone: class_name ='%sFrom%s'% (cls.__name__, queryset_class.__name__) class_dict = {'_queryset_class': queryset_class, } class_dict.update(cls._get_queryset_methods(queryset_class))# 重点关注returntype(class_name, (cls,), class_dict)# 重点关注
他返回的是一个用元类type定义的一个新的类,这个类继承自Manager类,同时把QuerySet类中的多有方法等属性也添加到了这个用type新建的类,具体体现就在cls._get_queryset_methods(queryset_class)。同样,在django/db/models/manager.py中找到_get_queryset_methods函数,就会明白:
@classmethoddef_get_queryset_methods(cls, queryset_class):defcreate_method(name, method):defmanager_method(self, *args, **kwargs):returngetattr(self.get_queryset(), name)(*args, **kwargs) manager_method.__name__ = method.__name__ manager_method.__doc__ = method.__doc__returnmanager_method
2.3 以models.objects.all()为例分析
2.3.1 从QuerySet的all方法开始
由前面的分析可以知道,这里的all其实也就是QuerySet中的all方法:
# django/db/models/query.pydefall(self):"""
Returns a new QuerySet that is a copy of the current one. This allows a
QuerySet to proxy for a model manager in some cases.
"""returnself._clone()
在django中,我们一般拿到all的返回值都是进行for循环读取每一个值,然后处理,所以,看看self._clone() 在for循环后得到了什么呢?,首先_clone方法返回的
def_clone(self, **kwargs): query = self.query.clone()ifself._sticky_filter: query.filter_is_sticky =Trueclone = self.__class__(model=self.model, query=query, using=self._db, hints=self._hints) clone._for_write = self._for_write clone._prefetch_related_lookups = self._prefetch_related_lookups[:] clone._known_related_objects = self._known_related_objects clone._iterable_class = self._iterable_class clone._fields = self._fields clone.__dict__.update(kwargs)returnclone
其实还是一个QuerySet实例,根据python中的魔法特性,对于一个类/对象来说,for循环会去调用他的__iter__方法,那么直接看QuerySet类中的__iter__方法就好了:
def__iter__(self):"""
The queryset iterator protocol uses three nested iterators in the
default case:
1. sql.compiler:execute_sql()
- Returns 100 rows at time (constants.GET_ITERATOR_CHUNK_SIZE)
using cursor.fetchmany(). This part is responsible for
doing some column masking, and returning the rows in chunks.
2. sql/compiler.results_iter()
- Returns one row at time. At this point the rows are still just
tuples. In some cases the return values are converted to
Python values at this location.
3. self.iterator()
- Responsible for turning the rows into model objects.
"""self._fetch_all()# 重点关注returniter(self._result_cache)
这里调用了_fetch_all()方法:
def_fetch_all(self):ifself._result_cacheisNone: self._result_cache =list(self.iterator())# 重点关注这个ifself._prefetch_related_lookupsandnotself._prefetch_done: self._prefetch_related_objects()
所以看iterator()方法就好:
defiterator(self):"""
An iterator over the results from applying this QuerySet to the
database.
"""returniter(self._iterable_class(self))
在QuerySet中的__init__方法中有self._iterable_class = ModelIterable,那么看ModelIterable类
2.3.2 在ModelIterable处获得obj
classModelIterable(BaseIterable):"""
Iterable that yields a model instance for each row.
"""def__iter__(self): queryset = self.queryset db = queryset.db compiler = queryset.query.get_compiler(using=db)# 重点关注# Execute the query. This will also fill compiler.select, klass_info,# and annotations.results = compiler.execute_sql()# 重点关注select, klass_info, annotation_col_map = (compiler.select, compiler.klass_info, compiler.annotation_col_map)ifklass_infoisNone:returnmodel_cls = klass_info['model']# models.py中的model类,详见django/db/models/sql/compiler.py中的 get_select函数(165行)select_fields = klass_info['select_fields']#model_fields_start, model_fields_end = select_fields[0], select_fields[-1] +1init_list = [f[0].target.attnameforfinselect[model_fields_start:model_fields_end]] related_populators = get_related_populators(klass_info, select, db)forrowincompiler.results_iter(results): obj = model_cls.from_db(db, init_list, row[model_fields_start:model_fields_end])#重点注意ifrelated_populators:forrel_populatorinrelated_populators: rel_populator.populate(row, obj)ifannotation_col_map:forattr_name, col_posinannotation_col_map.items():setattr(obj, attr_name, row[col_pos])# Add the known related objects to the model, if there are anyifqueryset._known_related_objects:forfield, rel_objsinqueryset._known_related_objects.items():# Avoid overwriting objects loaded e.g. by select_relatedifhasattr(obj, field.get_cache_name()):continuepk =getattr(obj, field.get_attname())try: rel_obj = rel_objs[pk]exceptKeyError:pass# may happen in qs1 | qs2 scenarioselse:setattr(obj, field.name, rel_obj)yieldobj
所以,其实我们在for 循环获取all的结果的时候,最后获得的就是obj,所以关注obj对象就好了,但是在这个过程中,我们貌似还不知道django是怎么确认到底要操作哪一个数据库的,他是怎么和数据库建立连接的呢?obj到底是怎么来的呢?
2.3.3 哪来的obj
其实对于django的mysql部分,在class ModelIterable(BaseIterable)的源码中,重点关注一下queryset.query.get_compiler(using=db)方法,也就是django/db/models/sql/query.py中的 get_compiler方法,这就是指定从哪个数据库中获取数据的入口。
get_compiler方法
defget_compiler(self, using=None, connection=None):ifusingisNoneandconnectionisNone:raiseValueError("Need either using or connection")ifusing:# 这里的connections是全局变量,见django/db/__init__.py中的connections = ConnectionHandler(),# 所以这里的connections[using]就会触发ConnectionHandler类的__getitem__方法(python的魔法函数之一)connection = connections[using]returnconnection.ops.compiler(self.compiler)(self, connection, using)
那再看看ConnectionHandler的__getitem__方法:
def__getitem__(self, alias):ifhasattr(self._connections, alias):returngetattr(self._connections, alias) self.ensure_defaults(alias) self.prepare_test_settings(alias)# self.databases就是settings中指定的settings.DATABASES,是一个字典db = self.databases[alias]# 加载指定的数据库后端引擎backend = load_backend(db['ENGINE'])# 参见django/db/backends/mysql/base.py的中的DatabaseWrapperconn = backend.DatabaseWrapper(db, alias)setattr(self._connections, alias, conn)returnconn
所以上面get_compiler函数中的connection变量是一个DatabaseWrapper类的实例。那 connection.ops.compiler(self.compiler)(self, connection, using)到底是个啥呢?先看看DatabaseWrapper类的ops是啥。
classDatabaseWrapper(BaseDatabaseWrapper): vendor ='mysql'#中间省略Database = Database SchemaEditorClass = DatabaseSchemaEditordef__init__(self, *args, **kwargs):super(DatabaseWrapper, self).__init__(*args, **kwargs) self.features = DatabaseFeatures(self) self.ops = DatabaseOperations(self)# 重点关注self.client = DatabaseClient(self) self.creation = DatabaseCreation(self) self.introspection = DatabaseIntrospection(self) self.validation = DatabaseValidation(self)
connection.ops其实是一个DatabaseOperations类的对象,对于DatabaseOperations类,他的源码,目前只需要关注
classDatabaseOperations(BaseDatabaseOperations): compiler_module ="django.db.backends.mysql.compiler"
所以接下来只要看看DatabaseOperations中的compiler方法了,DatabaseOperations中是没有compiler方法的,但是它继承自BaseDatabaseOperations,所以看看django/db/backends/base/operations.py中BaseDatabaseOperations的compiler方法
defcompiler(self, compiler_name):"""
Returns the SQLCompiler class corresponding to the given name,
in the namespace corresponding to the `compiler_module` attribute
on this backend.
"""ifself._cacheisNone: self._cache = import_module(self.compiler_module)returngetattr(self._cache, compiler_name)
对于mysql而言,从DatabaseOperations类的源码中可以看出,self.compiler_module就是 compiler_module = "django.db.backends.mysql.compiler",所以connection.ops.compiler(self.compiler),就是根据self.compiler从django/db/backends/mysql/compiler模块中返回一个SQLcompiler类或者其子类(根据query类型,也就是self.compiler)的实例(django/db/models/sql/compiler.py),SQLcompiler中的execute_sql方法中调用cursor = self.connection.cursor()
defexecute_sql(self, result_type=MULTI):"""
Run the query against the database and returns the result(s). The
return value is a single data item if result_type is SINGLE, or an
iterator over the results if the result_type is MULTI.
result_type is either MULTI (use fetchmany() to retrieve all rows),
SINGLE (only retrieve a single row), or None. In this last case, the
cursor is returned if any query is executed, since it's used by
subclasses such as InsertQuery). It's possible, however, that no query
is needed, as the filters describe an empty set. In that case, None is
returned, to avoid any unnecessary database interaction.
"""ifnotresult_type: result_type = NO_RESULTStry: sql, params = self.as_sql()ifnotsql:raiseEmptyResultSetexceptEmptyResultSet:ifresult_type == MULTI:returniter([])else:returncursor = self.connection.cursor()#重点注意try: cursor.execute(sql, params)exceptException: cursor.close()raiseifresult_type == CURSOR:# Caller didn't specify a result_type, so just give them back the# cursor to process (and close).returncursorifresult_type == SINGLE:try: val = cursor.fetchone()ifval:returnval[0:self.col_count]returnvalfinally:# done with the cursorcursor.close()ifresult_type == NO_RESULTS: cursor.close()returnresult = cursor_iter( cursor, self.connection.features.empty_fetchmany_value, self.col_count )ifnotself.connection.features.can_use_chunked_reads:try:# If we are using non-chunked reads, we return the same data# structure as normally, but ensure it is all read into memory# before going any further.returnlist(result)finally:# done with the cursorcursor.close()returnresult
折叠
因为在SQLCompiler中的__init__函数中有self.connection = connection,这里的connection就是DatabaseWrapper实例,所以cursor = self.connection.cursor()即是DatabaseWrapper中的cursor方法
defcursor(self):"""
Creates a cursor, opening a connection if necessary.
"""self.validate_thread_sharing()ifself.queries_logged: cursor = self.make_debug_cursor(self._cursor())else: cursor = self.make_cursor(self._cursor())returncursor
这里重点关注self._cursor()方法,即django/db/backends/base/base.py中的_corsor方法。
def_cursor(self): self.ensure_connection()# 重点关注withself.wrap_database_errors:returnself.create_cursor()
重点关注self.ensure_connection()方法
defensure_connection(self):"""
Guarantees that a connection to the database is established.
"""ifself.connectionisNone:withself.wrap_database_errors: self.connect()
self.connection 在BaseDatabaseWrapper的__init__函数中为None,所以执行self.connect()方法
Database就是MySQLdb
defconnect(self):"""Connects to the database. Assumes that the connection is closed."""# Check for invalid configurations.self.check_settings()# In case the previous connection was closed while in an atomic blockself.in_atomic_block =Falseself.savepoint_ids = [] self.needs_rollback =False# Reset parameters defining when to close the connectionmax_age = self.settings_dict['CONN_MAX_AGE'] self.close_at =Noneifmax_ageisNoneelsetime.time() + max_age self.closed_in_transaction =Falseself.errors_occurred =False# Establish the connectionconn_params = self.get_connection_params() self.connection = self.get_new_connection(conn_params)# 重点关注self.set_autocommit(self.settings_dict['AUTOCOMMIT']) self.init_connection_state() connection_created.send(sender=self.__class__, connection=self) self.run_on_commit = []
这里重点关注self.get_new_connection,这个函数其实是在django/db/backends/mysql/base.py中的146行的DatabaseWrapper中的get_new_connection被重写,所以也就是
defget_new_connection(self, conn_params): conn = Database.connect(**conn_params) conn.encoders[SafeText] = conn.encoders[six.text_type] conn.encoders[SafeBytes] = conn.encoders[bytes]returnconn
Database为import MySQLdb as Database,哈哈其实发现到最后django的ORM是调用第三方的库,connect方法就是MySQLdb/__init__.py中的Connect方法,那么最后连接到底是哪个mysql呢,最终就是看conn_params,在connect方法中可以发现conn_params = get_connection_params(),那么看看get_connection_params函数:
defget_connection_params(self): kwargs = {'conv': django_conversions,'charset':'utf8', }ifsix.PY2: kwargs['use_unicode'] =Truesettings_dict = self.settings_dictifsettings_dict['USER']: kwargs['user'] = settings_dict['USER']ifsettings_dict['NAME']: kwargs['db'] = settings_dict['NAME']ifsettings_dict['PASSWORD']: kwargs['passwd'] = force_str(settings_dict['PASSWORD'])ifsettings_dict['HOST'].startswith('/'): kwargs['unix_socket'] = settings_dict['HOST']elifsettings_dict['HOST']: kwargs['host'] = settings_dict['HOST']ifsettings_dict['PORT']: kwargs['port'] =int(settings_dict['PORT'])# We need the number of potentially affected rows after an# "UPDATE", not the number of changed rows.kwargs['client_flag'] = CLIENT.FOUND_ROWS kwargs.update(settings_dict['OPTIONS'])returnkwargs
全都来自settings_dict,我们再回到ConnectionHandler的__getitem__方法,
def__getitem__(self, alias):ifhasattr(self._connections, alias):returngetattr(self._connections, alias) self.ensure_defaults(alias) self.prepare_test_settings(alias) db = self.databases[alias] backend = load_backend(db['ENGINE']) conn = backend.DatabaseWrapper(db, alias)setattr(self._connections, alias, conn)returnconn
回过头来从上面可以看出,其实settings_dict = db = self.databases[alias],就是settings中DATABASES中定义alias的字典的值,alias默认为default,所以也就是类似下面的一个东东。
DATABASES = {'default': {'ENGINE':'django.db.backends.mysql','NAME':'dbname','USER':'dbuser','PASSWORD':'dbpasswd','HOST':'dbhost','PORT':'dbport', }}
所以SQLcompiler中的execute_sql方法中调用cursor = self.connection.cursor()中的cursor 就是django/db/backends/base/base.py中cursor方法的返回值,
defcursor(self):"""
Creates a cursor, opening a connection if necessary.
"""self.validate_thread_sharing()ifself.queries_logged: cursor = self.make_debug_cursor(self._cursor())else: cursor = self.make_cursor(self._cursor())returncursor
那么cursor到底是啥呢,其实make_cursor只是对self._cursor()做简单的封装,真正的执行时的cursor还是self._cursor()的返回值。
def_cursor(self): self.ensure_connection()withself.wrap_database_errors:returnself.create_cursor()
所以关注create_cursor方法就好。create_cursor是django/db/backends/mysql/base.py中的DatabaseWrapper中的create_cursor方法。
defcreate_cursor(self): cursor = self.connection.cursor()returnCursorWrapper(cursor)
所以从上面的分析中可以看出,self.connection是self.connect函数中创建的connection,也就是MySQLdb中的connection,所以cursor就是MySQLdb.connection.cursor,也就是根据setting.py中的DATABASES值指定的mysql数据库,建立的连接和游标。
对于django中的mysql来说,执行流程是,SQLcompliter或者其子类执行sql语句时,首先用cursor = self.connection.cursor()获取游标cursor,因为self.connection是DatabaseWrapper类,所以调用DatabaseWrapper的cursor函数,这个函数会根据传进来的db信息(settings中的DATABASES信息),首先去调用MySQLdb中的库新建一个connection连接,获取游标cursor,然后借用cursor去执行sql。其实mysql的查询的过程主要从query.get_compiler开始的,也就是queryset.query.get_compiler(using)。最后我们回到obj上来。
obj = model_cls.from_db(db, init_list, row[model_fields_start:model_fields_end]),关注from_db方法
deffrom_db(cls, db, field_names, values):iflen(values) !=len(cls._meta.concrete_fields): values =list(values) values.reverse() values = [values.pop()iff.attnameinfield_nameselseDEFERREDforfincls._meta.concrete_fields] new = cls(*values) new._state.adding =Falsenew._state.db = dbreturnnew
特喵的,到头来还是一个model对象,对象中的值来源于values,values 从哪儿来的呢?values = row[model_fields_start:model_fields_end],而row从哪儿来的?for row in compiler.results_iter(results):,也就是最终来源于results,results = compiler.execute_sql(),execute_sql就是去连接的数据库去查询结果,我们再看一遍execute_sql;
defexecute_sql(self, result_type=MULTI):"""
Run the query against the database and returns the result(s). The
return value is a single data item if result_type is SINGLE, or an
iterator over the results if the result_type is MULTI.
result_type is either MULTI (use fetchmany() to retrieve all rows),
SINGLE (only retrieve a single row), or None. In this last case, the
cursor is returned if any query is executed, since it's used by
subclasses such as InsertQuery). It's possible, however, that no query
is needed, as the filters describe an empty set. In that case, None is
returned, to avoid any unnecessary database interaction.
"""ifnotresult_type: result_type = NO_RESULTStry: sql, params = self.as_sql()ifnotsql:raiseEmptyResultSetexceptEmptyResultSet:ifresult_type == MULTI:returniter([])else:return#重点关注cursor = self.connection.cursor()try: cursor.execute(sql, params)exceptException: cursor.close()raiseifresult_type == CURSOR:# Caller didn't specify a result_type, so just give them back the# cursor to process (and close).returncursorifresult_type == SINGLE:try: val = cursor.fetchone()ifval:returnval[0:self.col_count]returnvalfinally:# done with the cursorcursor.close()ifresult_type == NO_RESULTS: cursor.close()return#重点关注result = cursor_iter( cursor, self.connection.features.empty_fetchmany_value, self.col_count )ifnotself.connection.features.can_use_chunked_reads:try:# If we are using non-chunked reads, we return the same data# structure as normally, but ensure it is all read into memory# before going any further.returnlist(result)finally:# done with the cursorcursor.close()returnresult
折叠
这里我们只要重点关注下面这段就好了。
result = cursor_iter(
cursor, self.connection.features.empty_fetchmany_value,
self.col_count
)
那么cursor_iter方法做了什么呢?
defcursor_iter(cursor, sentinel, col_count):"""
Yields blocks of rows from a cursor and ensures the cursor is closed when
done.
"""try:forrowsiniter((lambda: cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)), sentinel):yield[r[0:col_count]forrinrows]finally: cursor.close()
哈哈,看到这里还不明白的话,建议用MySQLdb库调用一下 cursor.fetchmany试试就知道了。cursor.fetchmany返回的就是从mysql中查询获取的结果。所以,其实也就是将从mysql中获取到的数据封装成对应的model对象。
2.4 以models.objects.first()为例分析
上面我们只分析常用的model.obejcts.all这一种情况,那么对于model.obejcts.first(),model.obejcts.get()等其他情况呢?
先看model.obejcts.first(),也就是看first函数:
deffirst(self):"""
Returns the first object of a query, returns None if no match is found.
"""objects =list((selfifself.orderedelseself.order_by('pk'))[:1])ifobjects:returnobjects[0]returnNone
这里重点关注objects[0],其实对于objects 不管是self ,还是self.order_by的返回值,他们都是一个QuerySet类,那么objects[0]就会调用django/db/models/query.py中159行的QuerySet的__getitem__函数:
def__getitem__(self, k):"""
Retrieves an item or slice from the set of results.
"""ifnotisinstance(k, (slice,) + six.integer_types):#k为 int,所以这里一定通过raiseTypeErrorassert((notisinstance(k,slice)and(k >=0))or(isinstance(k,slice)and(k.startisNoneork.start >=0)and(k.stopisNoneork.stop >=0))), \"Negative indexing is not supported."ifself._result_cacheisnotNone:returnself._result_cache[k]ifisinstance(k,slice): qs = self._clone()ifk.startisnotNone: start =int(k.start)else: start =Noneifk.stopisnotNone: stop =int(k.stop)else: stop =Noneqs.query.set_limits(start, stop)returnlist(qs)[::k.step]ifk.stepelseqs qs = self._clone() qs.query.set_limits(k, k +1)returnlist(qs)[0]
当self._result_cache不为空的时候,这部分就不在细说,当self._result_cache为None的时候,那么list(qs)[0]中的list()也会去调用qs(即QuerySet类),这里有体现了一个python的内置魔法,那就是list()会去调用qs对象中的__iter__函数;那么就看看QuerySet的__iter__()函数:
def__iter__(self):"""
The queryset iterator protocol uses three nested iterators in the
default case:
1. sql.compiler:execute_sql()
- Returns 100 rows at time (constants.GET_ITERATOR_CHUNK_SIZE)
using cursor.fetchmany(). This part is responsible for
doing some column masking, and returning the rows in chunks.
2. sql/compiler.results_iter()
- Returns one row at time. At this point the rows are still just
tuples. In some cases the return values are converted to
Python values at this location.
3. self.iterator()
- Responsible for turning the rows into model objects.
"""self._fetch_all()returniter(self._result_cache)
其实又回到for循环model.objects.all的常规用法了,后续就不再赘述。
2.5 以models.objects.get()为例分析
关于model.obejcts.get()这一类的操作,其实也是看get()函数,
defget(self, *args, **kwargs):"""
Performs the query and returns a single object matching the given
keyword arguments.
"""clone = self.filter(*args, **kwargs)ifself.query.can_filter()andnotself.query.distinct_fields: clone = clone.order_by() num =len(clone)#重点关注ifnum ==1:returnclone._result_cache[0]ifnotnum:raiseself.model.DoesNotExist("%s matching query does not exist."% self.model._meta.object_name )raiseself.model.MultipleObjectsReturned("get() returned more than one %s -- it returned %s!"% (self.model._meta.object_name, num) )
这里关注 num = len(clone),len()也会触发另外一个python魔法,那就是len()回去调用__len__()函数,所以看看def __len__(self)函数的源码:
def__len__(self): self._fetch_all()#重点关注returnlen(self._result_cache)
到了这里又跟上面的一样啦,因为get返回的是clone._result_cache[0],这里的_result_cache只不过是被self.filter函数给过滤后的结果。其实这里面所有的操作,只要关注self._fetch_all()被谁调用了,都能反向推到获得。
其实这里还有一点不明白,每次执行相同的execute_sql函数,他怎么知道是增删查改中的哪一种呢?对应的SQL语句从而来呢?其实看看execute_sql中调用的self.as_sql函数就好了。每一个继承自SQLCompiler的不同的类的as_sql返回的SQL语句都是特定的,只是要替换其中的表名等参数,例如:
classSQLDeleteCompiler(SQLCompiler):defas_sql(self):"""
Creates the SQL for this query. Returns the SQL string and list of
parameters.
"""assertlen([tfortinself.query.tablesifself.query.alias_refcount[t] >0]) ==1, \"Can only delete from one table at a time."qn = self.quote_name_unless_alias result = ['DELETE FROM %s'% qn(self.query.tables[0])] where, params = self.compile(self.query.where)ifwhere: result.append('WHERE %s'% where)return' '.join(result),tuple(params)classSQLUpdateCompiler(SQLCompiler):defas_sql(self):"""
Creates the SQL for this query. Returns the SQL string and list of
parameters.
"""self.pre_sql_setup()ifnotself.query.values:return'', () qn = self.quote_name_unless_alias values, update_params = [], []forfield, model, valinself.query.values:ifhasattr(val,'resolve_expression'): val = val.resolve_expression(self.query, allow_joins=False, for_save=True)ifval.contains_aggregate:raiseFieldError("Aggregate functions are not allowed in this query")elifhasattr(val,'prepare_database_save'):iffield.remote_field: val = field.get_db_prep_save( val.prepare_database_save(field), connection=self.connection, )else:raiseTypeError("Tried to update field %s with a model instance, %r. ""Use a value compatible with %s."% (field, val, field.__class__.__name__) )else: val = field.get_db_prep_save(val, connection=self.connection)# Getting the placeholder for the field.ifhasattr(field,'get_placeholder'): placeholder = field.get_placeholder(val, self, self.connection)else: placeholder ='%s'name = field.columnifhasattr(val,'as_sql'): sql, params = self.compile(val) values.append('%s = %s'% (qn(name), sql)) update_params.extend(params)elifvalisnotNone: values.append('%s = %s'% (qn(name), placeholder)) update_params.append(val)else: values.append('%s = NULL'% qn(name))ifnotvalues:return'', () table = self.query.tables[0] result = ['UPDATE %s SET'% qn(table),', '.join(values), ] where, params = self.compile(self.query.where)ifwhere: result.append('WHERE %s'% where)return' '.join(result),tuple(update_params + params)
折叠
什么时候调用那些不同的继承自SQLCompiler的类呢?在QuerySet中我们看update函数
defupdate(self, **kwargs):"""
Updates all elements in the current QuerySet, setting all the given
fields to the appropriate values.
"""assertself.query.can_filter(), \"Cannot update a query once a slice has been taken."self._for_write =Truequery = self.query.clone(sql.UpdateQuery)#重点注意query.add_update_values(kwargs)withtransaction.atomic(using=self.db, savepoint=False): rows = query.get_compiler(self.db).execute_sql(CURSOR) self._result_cache =Nonereturnrows update.alters_data =True
最后调用的是SQLUpdateCompiler类,所以query.get_compiler(self.db)其实是根据query的类型,Query的类型在django/db/models/sql/subquerys.py有定义。所以其实也就是query的类型决定了SQL语句,query的类型在那儿被决定的呢,就是在model.obejcts.get(pk).update,采用UpdateQuery,model.obejcts.get(pk).delete,其他同理。他只不过是将普通的Query类进行clone,然后更改自身的__class__属性。
2.6 小结
所以,整个django中mysql操作的关键是query.get_compiler(self.using).execute_sql();首先query在上面被对应的操作(update,delete, insert等)所决定,self.using决定操作的数据库,query类型决定execute_sql执行的SQL语句,所以整个就被决定下来了。
执行完操作的最后一步就是save()函数了,save()函数在django/db/models/base.py第718行,暂时觉得没啥需要将分析的,就不做过多的分析了。
其实ORM就是用某一种编程语言,基于该语言对应的数据库的库进行封装。
三 Qeuryset分析
每个Model都有一个默认的manager实例,名为objects。QuerySet有两种来源:通过manager的方法得到、通过QuerySet的方法得到。
mananger的方法和QuerySet的方法大部分同名,同意思,如filter(),update()等,但也有些不同,如manager有create()、get_or_create(),而QuerySet有delete()等,看源码就可以很容易的清楚Manager类与Queryset类的关系,Manager类的绝大部分方法是基于Queryset的。一个QuerySet包含一个或多个model instance。QuerySet类似于Python中的list,list的一些方法QuerySet也有,比如切片,遍历。
>>> fromuserex.modelsimportUserEx>>> type(UserEx.objects)>>> a = UserEx.objects.all()>>> type(a)
QuerySet是延迟获取的,只有当用到这个QuerySet时,才会查询数据库求值。也就是懒加载!
另外,查询到的QuerySet又是缓存的,当再次使用同一个QuerySet时,并不会再查询数据库,而是直接从缓存获取(不过,有一些特殊情况)。一般而言,当对一个没有求值的QuerySet进行的运算,返回的是QuerySet、ValuesQuerySet、ValuesListQuerySet、Model实例时,一般不会立即查询数据库;反之,当返回的不是这些类型时,会查询数据库。下面介绍几种(并非全部)对QuerySet求值的场景。
3.1 QuerySet求值的场景
classBlog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField()def__unicode__(self):returnself.nameclassAuthor(models.Model): name = models.CharField(max_length=50) email = models.EmailField()def__unicode__(self):returnself.nameclassEntry(models.Model): blog = models.ForeignKey(Blog) headline = models.CharField(max_length=255) body_text = models.TextField() pub_date = models.DateField() mod_date = models.DateField() authors = models.ManyToManyField(Author) n_comments = models.IntegerField() n_pingbacks = models.IntegerField() rating = models.IntegerField()def__unicode__(self):returnself.headline
遍历queryset
a = Entry.objects.all()foreina:print(e.headline)
当遍历一开始时,先从数据库执行查询select * from Entry得到a,然后再遍历a。注意:这里只是查询Entry表,返回的a的每条记录只包含Entry表的字段值,不管Entry的model中是否有onetoone、onetomany、manytomany字段,都不会关联查询。这遵循的是数据库最少读写原则。我们修改一下代码,如下,遍历一开始也是先执行查询得到a,但当执行print (e.blog.name)时,还需要再次查询数据库获取blog实体。
fromdjango.dbimportconnectionl = connection.queries#l是一个列表,记录SQL语句a = Entry.objects.all()foreina:print(e.blog.name)len(l)
遍历时,每次都要查询数据库,l长度每次增1,Django提供了方法可以在查询时返回关联表实体,如果是onetoone或onetomany,那用select_related,不过对于onetomany,只能在主表(定义onetomany关系的那个表)的manager中使用select_related方法,即通过select_related获取的关联对象是model instance,而不能是QuerySet,如下,e.blog就是model instance。对于onetomany的反向和manytomany,要用prefetch_related,它返回的是多条关联记录,是QuerySet。
a = Entry.objects.select_related('blog')foreina:print(e.blog.name)len(l)
可以看到从开始到结束,l的长度只增加1。另外,通过查询connection.queries[-1]可以看到Sql语句用了join。
queryset切片
切片不会立即执行,除非显示指定了步长,如:
a= Entry.objects.all()[0:10:2]
步长为2。
序列化,即Pickling
序列化QuerySet很少用。
repr()
和str()功能相似,将对象转为字符串,很少用。
len()
计算QuerySet元素的数量,并不推荐使用len(),除非QuerySet是求过值的(即evaluated),否则,用QuerySet.count()获取元素数量,这个效率要高。
list()
将QuerySet转为list。list(qs)
bool()
判断qs是否为空。
ifEntry.objects.filter(headline="Test"):print("There is at least one Entry with the headline Test")
同样不建议这种方法判断是否为空,而应该使用QuerySet.exists(),查询效率高。
3.2 queryset的方法
数据库的常用操作就四种:增、删、改、查,QuerySet的方法涉及删、改、查。后面还会讲model对象的方法,model方法主要是增、删、改、还有调用model实例的字段。
3.2.1 delete()
原型:delete()
返回:None
相当于delete-from-where, delete-from-join-where。先filter,然后对得到的QuerySet执行delete()方法就行了,它会同时删除关联它的那些记录,比如我删除记录表1中的A记录,表2中的B记录中有A的外键,那同时也会删除B记录,那ManyToMany关系呢?对于ManyToMany,删除其中一方的记录时,会同时删除中间表的记录,即删除双方的关联关系。由于有些数据库,如Sqlite不支持delete与limit连用,所以在这些数据库对QuerySet的切片执行delete()会出错。
>>> a = UserEx.objects.filter(is_active=False)>>> b = a[:3]>>> b.delete()#执行时会报错解决:UserEx.objects.filter(pk__in=b).delete()
我们在用django开发项目的的时候,经常要和数据库打交道,而django操作数据库非常的方便,有很多非常简便的方法让你能够快速的从数据库里获得你想要的数据。今天我就介绍给大家一个很好用的方法,那就是django in操作了我们经常查数据库的时候要把几个符合条件的记录都给查出来,那就要用到sql语句的in操作,那django怎么来执行数据库的in操作呢?接着看下面把。有2个方法可以很好的实现:1直接用filter语句里的方法来实现2用到extra方法比如我们要执行:
select *fromtable whereidin(3,4,5,20)
用上面2个方法分别怎么操作呢?
djangofilter:Blog.objects.filter(pk__in=[3,4,5,20])django extra:Blog.objects.extra(where=['id IN (3, 4, 5, 20)'])
3.2.2 update()
批量修改,返回修改的记录数。不过update()中的键值对的键只能是主表中的字段,不能是关联表字段,如下:
Entry.objects.update(blog__name='foo')#错误,无法修改关联表字段,只能修改Entry表的字段Entry.objects.filter(blog__name='foo').update(comments_on=False)#正确
最好的方法是先filter,查询出QuerySet,然后再执行QuerySet.update()。
由于有些数据库,不支持update与limit连用,所以在这些数据库对QuerySet的切片执行update()会出错。
3.2.3 filter(**kwargs)、exclude(**kwargs)、get(**kwargs)
相当于select-from-where,select-from-join-where,很多网站读数据库操作最多。可以看到,filter()的参数是变个数的键值对,而不会出现>,<,!=等符号,这些符号分别用__gt,__lt,~Q或exclude(),不过对于!=,建议使用Q查询,更不容易出错。可以使用双下划线对OneToOne、OneToMany、ManyToMany进行关联查询和反向关联查询,而且方法都是一样的,如:
>>> Entry.objects.filter(blog__name='Beatles Blog')#限定外键表的字段#下面是反向连接,不过要注意,这里不是entry_set,entry_set是Blog instance的一个属性,代表某个Blog object#的关联的所有entry,而QuerySet的方法中反向连接是直接用model的小写,不要把两者搞混。It works backwards,#too. To refer to a “reverse” relationship, just use the lowercase name of the model.>>> Blog.objects.filter(entry__headline__contains='Lennon')>>> Blog.objects.filter(entry__authors__name='Lennon')#ManyToMany关系,反向连接>>> myblog = Blog.objects.get(id=1)>>> Entry.objects.filter(blog=myblog)#正向连接。与下面一句等价,既可以用实体,也可以用#实体的主键,其实即使用实体,也是只用实体的主键而已。这两种方式对OneToOne、#OneToMany、ManyToMany的正向、反向连接都适用。>>> Entry.objects.filter(blog=1)#我个人不建议这样用,对于create(),不支持这种用法>>> myentry = Entry.objects.get(id=1)>>> Blog.objects.filter(entry=myentry)#ManyToMany反向连接。与下面两种方法等价>>> Blog.objects.filter(entry=1)>>> Blog.objects.filter(entry_id=1)#适用于OneToOne和OneToMany的正向连接OneToOne的关系也是这样关联查询,可以看到,Django对OneToOne、OneToMany、ManyToMany关联查询及其反向关联查询提供了相同的方式,真是牛逼啊。对于OneToOne、OneToMany的主表,也可以使用下面的方式Entry.objects.filter(blog_id=1),因为blog_id是数据库表Entry的一个字段, 这条语句与Entry.objects.filter(blog=blog1)生成的SQL是完全相同的。
与filter类似的还有exclude(**kwargs)方法,这个方法是剔除,相当于select-from-where not。可以使用双下划线对OneToOne、OneToMany、ManyToMany进行关联查询和反向关联查询,方法与filter()中的使用方法相同。
>>> Entry.objects.exclude(pub_date__gt=datetime.date(2005,1,3), headline='Hello')转为SQL为SELECT *FROM EntryWHERE NOT (pub_date >'2005-1-3'AND headline ='Hello')
3.2.4 SQL其它关键字在django中的实现
在SQL中,很多关键词在删、改、查时都是可以用的,如order by、 like、in、join、union、and、or、not等等,我们以查询为例,说一下django如何映射SQL的这些关键字的(查、删、改中这些关键字的使用方法基本相同)。
F类(无对应SQL关键字)
前面提到的filter/exclude中的查询参数值都是常量,如果我们想比较model的两个字段怎么办呢?Django也提供了方法,F类,F类实例化时,参数也可以用双下划线,也可以逻辑运算,如下:
>>> fromdjango.db.modelsimportF>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))>>> fromdatetimeimporttimedelta>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))>>> Entry.objects.filter(authors__name=F('blog__name'))
Q类(对应and/or/not)
如果有or等逻辑关系呢,那就用Q类,filter中的条件可以是Q对象与非Q查询混和使用,但不建议这样做,因为混和查询时Q对象要放前面,这样就有难免忘记顺序而出错,所以如果使用Q对象,那就全部用Q对象。Q对象也很简单,就是把原来filter中的各个条件分别放在一个Q()即可,不过我们还可以使用或与非,分别对应符号为”|”和”&”和”~”,而且这些逻辑操作返回的还是一个Q对象,另外,逗号是各组条件的基本连接符,也是与的关系,其实可以用&代替(在python manage.py shell测试过,&代替逗号,执行的SQL是一样的),不过那样的话可读性会很差,这与我们直接写SQL时,各组条件and时用换行一样,逻辑清晰:
fromdjango.db.modelsimportQ>>> Poll.objects.get( Q(pub_date=date(2005,5,2)) | Q(pub_date=date(2005,5,6)),question__startswith='Who')#正确,但不要这样混用>>> Poll.objects.get( Q(pub_date=date(2005,5,2)) | Q(pub_date=date(2005,5,6)),Q(question__startswith='Who'))#推荐,全部是Q对象>>> Poll.objects.get( (Q(pub_date=date(2005,5,2)) | Q(pub_date=date(2005,5,6)))&Q(question__startswith='Who'))#与上面语句同意,&代替”,”,可读性差Q类中时应该可以用F类,待测试。
annotate(无对应SQL关键字)
函数原型annotate(*args, **kwargs)
返回QuerySet
往每个QuerySet的model instance中加入一个或多个字段,字段值只能是聚合函数,因为使用annotate时,会用group by,所以只能用聚合函数。聚合函数可以像filter那样关联表,即在聚合函数中,Django对OneToOne、OneToMany、ManyToMany关联查询及其反向关联提供了相同的方式,见下面例子。
>>> fromdjango.contrib.auth.modelsimportUser>>> fromdjango.db.modelsimportCount#计算每个用户的userjob数量,字段命名为ut_num,返回的QuerySet中的每个object都有#这个字段。在UserJob中定义User为外键,在Job中定义与User是ManyToMany>>> a = User.objects.filter(is_active=True, userjob__is_active=True). annotate(n=Count(‘userjob’))#一对多反向连接>>> b = User.objects.filter(is_active=True, job__is_active=True).annotate(n=Count(‘job__name’))#多对多反向连接,User与Job是多对多>>> len(a)#这里才会对a求值>>> len(b)#这里才会对b求值
a对应的SQL语句为(SQL中没有为表起别名,u、ut是我加的):
selectauth.user.*,Count(ut.id)asut_numfromauth_userasuleftouterjoinjob_userjobasutonu.id=ut.user_idwhereu.is_active=Trueandut.is_active=Truegroupbyu.*
b对应的SQL语句为(SQL中没有为表起别名,u、t、r是我加的):
selectu.*,Count(t.name)asnfromauth_userasuleftouterjoinjob_job_usersasronu.id=r.user_idleftouterjoinjob_jobastonr.job_id=t.idwheret.is_active=Trueandu.is_active=Truegroupbyu.*
order_by——对应order by
函数原型 order_by(*fields)
返回QuerySet
正向的反向关联表跟filter的方式一样。如果直接用字段名,那就是升序asc排列;如果字段名前加-,就是降序desc
distinct——对应distinct
原型 distinct()
一般与values()、values_list()连用,这时它返回ValuesQuerySet、ValuesListQuerySet
这个类跟列表很相似,它的每个元素是一个字典。它没有参数(其实是有参数的,不过,参数只在PostgreSQL上起作用)。使用方法为:
>>> a=Author.objects.values_list(name).distinct()>>> b=Author.objects.values_list(name,email).distinct()
对应的SQL分别为:
selectdistinctnamefromAuthor
和
selectdistinctname,emailfromAuthor
distinct的了解:http://www.cnblogs.com/rainman/archive/2013/05/03/3058451.html
values()和values_list()——对应select 某几个字段
可以参考的连接:http://blog.csdn.net/tmpbook/article/details/50297403
函数原型values(*field), values_list(*field)
返回ValuesQuerySet, ValuesListQuerySet
Author.objects.filter(**kwargs)对应的SQL只返回主表(即Author表)的所有字段值,即使在查询时关联了其它表,关联表的字段也不会返回,只有当我们通过Author instance用关联表时,Django才会再次查询数据库获取值。当我们不用Author instance的方法,且只想返回几个字段时,就要用values(),它返回的是一个ValuesQuerySet对象,它类似于一个列表,不过,它的每个元素是字典。而values_list()跟values()相似,它返回的是一个ValuesListQuerySet,也类型于一个列表,不过它的元素不是字典,而是元组。一般的,当我们不需要model instance的方法且返回多个字段时,用values(*field),而返回单个字段时用values_list(‘field’,flat=True),这里flat=True是要求每个元素不是元组,而是单个值,见下面例子。而且我们可以返回关联表的字段,用法跟filter中关联表的方式完全相同。
>>> a = User.objects.values(‘id’,’username’,’userex__age’)>>> type(a)>>> a[{‘id’:0,’username’:u’test0’,’ userex__age’:20},{‘id’:1,’username’:u’test1’,’userex__age’:25}, {‘id’:2,’username’:u’test2’, ’ userex__age’:28}]>>> b= User.objects.values_list(’username’,flat=True)>>> b[u’test0’, u’test1’ ,u’test2’]
select_related()——对应返回关联记录实体
原型select_related(*filed)
返回QuerySet
它可以指定返回哪些关联表model instance,这里的field跟filter()中的键一样,可以用双下划线,但也有不同,You can refer to any ForeignKey or OneToOneField relation in the list of fields passed to select_related(),QuerySet中的元素中的OneToOne关联及外键对应的是都是关联表的一条记录,如my_entry=Entry.objects.get(id=1),my_entry.blog就是关联表的一条记录的对象。select_related()不能用于OneToMany的反向连接,和ManyToMany,这些都是model的一条记录对应关联表中的多条记录。前面提到了对于a = Author.objects.filter(**kwargs)这类语句,对应的SQL只返回主表,即Author的所有字段,并不会返回关联表字段值,只有当我们使用关联表时才会再查数据库返回,但有些时候这样做并不好。看下面两段代码,这两段代码在1.1中提到过。在代码1中,在遍历a前,先执行a对应的SQL,拿到数据后,然后再遍历a,而遍历过程中,每次都还要查询数据库获取关联表。代码2中,当遍历开始前,先拿到Entry的QuerySet,并且也拿到这个QuerySet的每个object中的blog对象,这样遍历过程中,就不用再查询数据库了,这样就减少了数据库读次数。
代码1a = Entry.objects.all()foreina:print(e.blog.name) 代码2a = Entry.objects.select_related('blog')foreina:print(e.blog.name)
prefetch_related(*field) ——对应返回关联记录实体的集合
函数原型prefetch_related(*field)
返回的是QuerySet
这里的field跟filter()中的键一样,可以用双下划线。用于OneToMany的反向连接,及ManyToMany。其实,prefetch_related()也能做select_related()的事情,但由于策略不同,可能相比select_related()要低效一些,所以建议还是各管各擅长的。select_related是用select ……join来返回关联的表字段,而prefetch_related是用多条SQL语句的形式查询,一般,后一条语句用IN来调用上一句话返回的结果。
classRestaurant(models.Model): pizzas = models.ManyToMany(Pizza, related_name='restaurants') best_pizza = models.ForeignKey(Pizza, related_name='championed_by')>>> Restaurant.objects.prefetch_related('pizzas__toppings')>>> Restaurant.objects.select_related('best_pizza').prefetch_related('best_pizza__toppings')
先用select_related查到best_pizza对象,再用prefetch_related 从best_pizza查出toppings
extra()——实现复杂的where子句
函数原型:extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
基本上,查询时用django提供的方法就够用了,不过有时where子句中包含复杂的逻辑,这种情况下django提供的方法可能不容易做到,还好,django有extra(), extra()中直接写一些SQL语句。不过,不同的数据库用的SQL有些差异,所以尽可能不要用extra()。需要时再看使用方法吧。
aggregate(*args, **kwargs)——对应聚合函数
参数为聚合函数,最好用**kwargs的形式,每个参数起一个名字。
该函数与annotate()有何区别呢?annotate相当于aggregate()和group by的结合,对每个group执行aggregate()函数。而单独的aggregate()并没有group by。
>>> fromdjango.db.modelsimportCount>>> q = Blog.objects.aggregate(Count('entry'))#这是用*args的形式,最好不要这样用>>> q = Blog.objects.aggregate(number_of_entries=Count('entry'))#这是用**kwargs的形式{'number_of_entries':16}
至此,我们总结了QuerySet方法返回的数据形式,主要有五种。第一种:返回QuerySet,每个object只包含主表字段;第二种:返回QuerySet,每个object除了包含主表所有字段,还包含某些关联表的object,这种情况要用select_related()和prefetch_related(),可以是任意深度(即任意多个双下划线)的关联,通常一层关联和二层关联用的比较多;第三种:返回ValuesQuerySet, ValuesListQuerySet,它们的每个元素包含若干主表和关联表的字段,不包含任何实体和关联实例,这种情况要用values()和values_list();第四种:返回model instance;第五种:单个值,如aggregate()方法。
exists()、count()、len()
如果只是想知道一个QuerySet是否为空,而不想获取QuerySet中的每个元素,那就用exists(),它要比len()、count()、和直接进行if判断效率高。如果只想知道一个QuerySet有多大,而不想获取QuerySet中的每个元素,那就用count();如果已经从数据库获取到了QuerySet,那就用len()。
上面也有提到。
contains/startswith/endswith——对应like
字段名加双下划线,除了它,还有icontains,即Case-insensitive contains,这个是大小写不敏感的,这需要相应数据库的支持。有些数据库需要设置才能支持大小写敏感。
in——对应in
字段名加双下划线。
exclude(field__in=iterable)——对应not in
iterable是可迭代对象
gt/gte/lt/lte——对应于`>,>=,<,<=
字段名加双下划线
range——对应于between and
字段名加双下划线,range后面值是列表
isnull——对应于is null
Entry.objects.filter(pub_date__isnull=True)对应的SQL为SELECT ... WHERE pub_date IS NULL;
QuerySet切片——对应于limit
QuerySet的索引只能是非负整数,不支持负整数,所以QuerySet[-1]错误
a=Entry.objects.all()[5:10]b=len(a)
执行Entry.objects.all()[5:8],对于不同的数据库,SQL语句不同,
Sqlite 的SQL语句为select*fromtablename limit3offset5; MySQL的SQL语句为select*fromtablename limit5,3;
3.2.5 经验
queryset 的关联查询:
qs = Disk.objects.filter(hostType=obj).values("id","diskEssenceType__name","diskOsType__name","hostType__name")
classHostTypeSerializer(ModelSerializer): cputypes = CPUTypeSerializer(many=True, read_only=True) memtypes = MEMTypeSerializer(many=True, read_only=True) bandwidths = BandWidthTypeSerializer(many=True, read_only=True)#disks = DiskSerializer(many=True, read_only=True)disks = serializers.SerializerMethodField()classMeta: model = HostType fields = ["id","name","cputypes","memtypes","bandwidths","disks", ]defget_disks(self, obj): qs = Disk.objects.filter(hostType=obj).values("id","diskEssenceType__name","diskOsType__name","hostType__name")dict= {"系统盘": [],"数据盘": []}foriteminqs:ifitem['diskOsType__name'] =="系统盘":dict["系统盘"].append(item)elifitem['diskOsType__name'] =="数据盘":dict["数据盘"].append(item) disk_list = [{"id":1,"type":"系统盘","details":[]}, {"id":2,"type":"数据盘","details":[]}]foros_diskindict["系统盘"]: disk_list[0]["details"].append(os_disk)fordata_distindict["数据盘"]: disk_list[1]["details"].append(data_dist)returndisk_list