Django - ORM源碼分析

轉發(fā):ORM源碼分析

目錄

Django - ORM分析

一 Django ORM

二 Manager分析

2.1?model中的manager從哪兒來--ModelBase

2.2?Manager

2.3 以models.objects.all()為例分析

2.3.1 從QuerySet的all方法開始

2.3.2 在ModelIterable處獲得obj

2.3.3 哪來的obj

get_compiler方法

Database就是MySQLdb

2.4 以models.objects.first()為例分析

2.5 以models.objects.get()為例分析

2.6 小結

三 Qeuryset分析

3.1?QuerySet求值的場景

遍歷queryset

queryset切片

序列化,即Pickling

repr()

len()

list()

bool()

3.2 queryset的方法

3.2.1 delete()

3.2.2 update()

3.2.3?filter(**kwargs)、exclude(**kwargs)队询、get(**kwargs)

3.2.4 SQL其它關鍵字在django中的實現

F類(無對應SQL關鍵字)

Q類(對應and/or/not)

annotate(無對應SQL關鍵字)

order_by——對應order by

distinct——對應distinct

values()和values_list()——對應select 某幾個字段

select_related()——對應返回關聯記錄實體

prefetch_related(*field)?——對應返回關聯記錄實體的集合

extra()——實現復雜的where子句

aggregate(*args, **kwargs)——對應聚合函數

exists()博烂、count()逸贾、len()

contains/startswith/endswith——對應like

in——對應in

exclude(field__in=iterable)——對應not in

gt/gte/lt/lte——對應于`>,>=,<,<=

range——對應于between and

isnull——對應于is null

QuerySet切片——對應于limit

3.2.5 經驗

queryset?的關聯查詢:

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方法最后創(chuàng)建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循環(huán)讀取每一個值责鳍,然后處理,所以兽间,看看self._clone()?在for循環(huán)后得到了什么呢历葛?,首先_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循環(huán)會去調用他的__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 循環(huán)獲取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]就會觸發(fā)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,所以執(zhí)行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,哈哈其實發(fā)現到最后django的ORM是調用第三方的庫航罗,connect方法就是MySQLdb/__init__.py中的Connect方法禀横,那么最后連接到底是哪個mysql呢,最終就是看conn_params粥血,在connect方法中可以發(fā)現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()做簡單的封裝觉吭,真正的執(zhí)行時的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函數中創(chuàng)建的connection培遵,也就是MySQLdb中的connection浙芙,所以cursor就是MySQLdb.connection.cursor,也就是根據setting.py中的DATABASES值指定的mysql數據庫籽腕,建立的連接和游標嗡呼。

對于django中的mysql來說,執(zhí)行流程是皇耗,SQLcompliter或者其子類執(zhí)行sql語句時南窗,首先用cursor = self.connection.cursor()獲取游標cursor,因為self.connection是DatabaseWrapper類廊宪,所以調用DatabaseWrapper的cursor函數矾瘾,這個函數會根據傳進來的db信息(settings中的DATABASES信息),首先去調用MySQLdb中的庫新建一個connection連接箭启,獲取游標cursor,然后借用cursor去執(zhí)行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循環(huán)model.objects.all的常規(guī)用法了李剖,后續(xù)就不再贅述。

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()也會觸發(fā)另外一個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()被誰調用了观挎,都能反向推到獲得琴儿。

其實這里還有一點不明白段化,每次執(zhí)行相同的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執(zhí)行的SQL語句像啼,所以整個就被決定下來了俘闯。

執(zhí)行完操作的最后一步就是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)

當遍歷一開始時迈着,先從數據庫執(zhí)行查詢select * from Entry得到a竭望,然后再遍歷a。注意:這里只是查詢Entry表裕菠,返回的a的每條記錄只包含Entry表的字段值咬清,不管Entry的model中是否有onetoone、onetomany、manytomany字段旧烧,都不會關聯查詢影钉。這遵循的是數據庫最少讀寫原則。我們修改一下代碼掘剪,如下平委,遍歷一開始也是先執(zhí)行查詢得到a,但當執(zhí)行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切片

切片不會立即執(zhí)行,除非顯示指定了步長期升,如:

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執(zhí)行delete()方法就行了,它會同時刪除關聯它的那些記錄副砍,比如我刪除記錄表1中的A記錄衔肢,表2中的B記錄中有A的外鍵,那同時也會刪除B記錄址晕,那ManyToMany關系呢膀懈?對于ManyToMany,刪除其中一方的記錄時谨垃,會同時刪除中間表的記錄启搂,即刪除雙方的關聯關系。由于有些數據庫刘陶,如Sqlite不支持delete與limit連用胳赌,所以在這些數據庫對QuerySet的切片執(zhí)行delete()會出錯。

>>> a = UserEx.objects.filter(is_active=False)>>> b = a[:3]>>> b.delete()#執(zhí)行時會報錯解決:UserEx.objects.filter(pk__in=b).delete()

我們在用django開發(fā)項目的的時候匙隔,經常要和數據庫打交道疑苫,而django操作數據庫非常的方便,有很多非常簡便的方法讓你能夠快速的從數據庫里獲得你想要的數據。今天我就介紹給大家一個很好用的方法捍掺,那就是django in操作了我們經常查數據庫的時候要把幾個符合條件的記錄都給查出來撼短,那就要用到sql語句的in操作,那django怎么來執(zhí)行數據庫的in操作呢挺勿?接著看下面把曲横。有2個方法可以很好的實現:1直接用filter語句里的方法來實現2用到extra方法比如我們要執(zhí)行:

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麦备,然后再執(zhí)行QuerySet.update()孽椰。

由于有些數據庫,不支持update與limit連用泥兰,所以在這些數據庫對QuerySet的切片執(zhí)行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等等风范,我們以查詢?yōu)槔f一下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測試過,&代替逗號堤如,執(zhí)行的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前,先執(zhí)行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()有何區(qū)別呢构捡?annotate相當于aggregate()和group by的結合,對每個group執(zhí)行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)

執(zhí)行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

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末饺鹃,一起剝皮案震驚了整個濱河市莫秆,隨后出現的幾起案子,更是在濱河造成了極大的恐慌悔详,老刑警劉巖馏锡,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異伟端,居然都是意外死亡,警方通過查閱死者的電腦和手機匪煌,發(fā)現死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門责蝠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人萎庭,你說我怎么就攤上這事霜医。” “怎么了驳规?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵肴敛,是天一觀的道長。 經常有香客問我,道長医男,這世上最難降的妖魔是什么砸狞? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮镀梭,結果婚禮上刀森,老公的妹妹穿的比我還像新娘。我一直安慰自己报账,他們只是感情好研底,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著透罢,像睡著了一般榜晦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上羽圃,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天乾胶,我揣著相機與錄音,去河邊找鬼统屈。 笑死胚吁,一個胖子當著我的面吹牛,可吹牛的內容都是我干的愁憔。 我是一名探鬼主播腕扶,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼吨掌!你這毒婦竟也來了半抱?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤膜宋,失蹤者是張志新(化名)和其女友劉穎窿侈,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體秋茫,經...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡史简,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了肛著。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片圆兵。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖枢贿,靈堂內的尸體忽然破棺而出殉农,到底是詐尸還是另有隱情,我是刑警寧澤局荚,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布超凳,位于F島的核電站愈污,受9級特大地震影響,放射性物質發(fā)生泄漏轮傍。R本人自食惡果不足惜暂雹,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望金麸。 院中可真熱鬧擎析,春花似錦、人聲如沸挥下。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽棚瘟。三九已至现斋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間偎蘸,已是汗流浹背庄蹋。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留迷雪,地道東北人限书。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像章咧,于是被迫代替她去往敵國和親倦西。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內容

  • 在django中常常見到modle.objects.using(db).all()或者modle.objects....
    llicety閱讀 4,990評論 1 6
  • 這部分標題比較大赁严,按照之前的分析方法肯定會比較復雜且不夠系統扰柠,所以從另一個角度出發(fā),我們通過對幾個關鍵問題的追溯來...
    minhelloworld閱讀 2,671評論 0 2
  • 前言 Django框架功能齊全自帶數據庫操作功能疼约,本文主要介紹Django的ORM框架 https://www.c...
    許華鋒閱讀 1,909評論 0 0
  • __exact 精確等于 like 'aaa'__iexact 精確等于 忽略大小寫 ilike'aaa'...
    amazing_bing閱讀 711評論 0 0
  • 一卤档、字段 AutoField(Field) - int自增列,必須填入參數 primary_key=T...
    許華鋒閱讀 1,430評論 0 0