Skip to content

Loading…

[Soc2014] The new Options API #2894

Closed
wants to merge 355 commits into from

9 participants

@PirosB3

Main API

Official Wiki page: https://code.djangoproject.com/wiki/new_meta_api

get_fields(m2m, data, related_m2m, related_objects, virtual,
include_parents, include_non_concrete, include_hidden, include_proxy) -> (field, field, ...)

get_field(field_name, m2m, data, related_m2m, related_objects, virtual) -> field

field_names -> (field_name, field_name, ..)

Cached properties for fast access

fields -> (field, field, ..)
concrete_fields -> (field, field, ...)
local_concrete_fields -> (field, field, ...)
field_names (field_name, field_name, ..)

Benchmarks

(compared to master)

Benchmark Name Control Experiment Diff
url_resolve_flat_i18n_off 0.3224664569 0.3128736854 0.0095927715 (2.97% faster)
url_resolve_flat 0.2262115002 0.2201457143 0.0060657859 (2.68% faster)
query_prefetch_related 0.1185187101 0.1131614804 0.0053572297 (4.52% faster)
query_all 0.0260368466 0.0244047880 0.0016320586 (6.27% faster)
query_select_related 0.0590585470 0.0582756639 0.0007828832 (1.33% faster)
query_raw 0.0150311589 0.0149269700 0.0001041889 (0.69% faster)
query_distinct 0.0003427148 0.0003322124 0.0000105023 (3.06% faster)
url_resolve 0.0174066782 0.0174011946 0.0000054836 (0.03% faster)
template_render_simple 0.0001164556 0.0001118183 0.0000046372 (3.98% faster)
query_delete_related 0.0003562689 0.0003521204 0.0000041485 (1.16% faster)
form_create 0.0001154184 0.0001116633 0.0000037551 (3.25% faster)
model_creation 0.0002620935 0.0002592921 0.0000028014 (1.07% faster)
query_iterator 0.0002910018 0.0002886057 0.0000023961 (0.82% faster)
form_clean 0.0000283957 0.0000273466 0.0000010490 (3.69% faster)
url_resolve_nested 0.0001253605 0.0001250029 0.0000003576 (0.29% faster)
query_delete 0.0002635837 0.0002634764 0.0000001073 (0.04% faster)
raw_sql 0.0000513077 0.0000515938 -0.0000002861 (0.56% slower)
template_render 0.0123201489 0.0123205304 -0.0000003815 (0.00% slower)
query_none 0.0001389146 0.0001396894 -0.0000007749 (0.56% slower)
query_all_multifield 0.0442516685 0.0442527294 -0.0000010610 (0.00% slower)
multi_value_dict 0.0001797199 0.0001827717 -0.0000030518 (1.70% slower)
query_count 0.0002346754 0.0002384424 -0.0000037670 (1.61% slower)
locale_from_request 0.0008673787 0.0008715868 -0.0000042081 (0.49% slower)
query_exclude 0.0005133271 0.0005178809 -0.0000045538 (0.89% slower)
query_latest 0.0003925323 0.0003977776 -0.0000052452 (1.34% slower)
model_delete 0.0004462004 0.0004532099 -0.0000070095 (1.57% slower)
query_order_by 0.0003699422 0.0003770232 -0.0000070810 (1.91% slower)
query_aggregate 0.0003185034 0.0003258467 -0.0000073433 (2.31% slower)
default_middleware 0.0004270792 0.0004346967 -0.0000076175 (1.78% slower)
query_filter 0.0004306793 0.0004388094 -0.0000081301 (1.89% slower)
query_update 0.0002333641 0.0002430677 -0.0000097036 (4.16% slower)
query_in_bulk 0.0004901409 0.0005011201 -0.0000109792 (2.24% slower)
query_dates 0.0005465746 0.0005576730 -0.0000110984 (2.03% slower)
query_annotate 0.0006684303 0.0006795764 -0.0000111461 (1.67% slower)
query_values 0.0003065467 0.0003197312 -0.0000131845 (4.30% slower)
query_complex_filter 0.0001333117 0.0001471758 -0.0000138640 (10.40% slower)
query_values_list 0.0002993703 0.0003140211 -0.0000146508 (4.89% slower)
template_compilation 0.0005861163 0.0006127477 -0.0000266314 (4.54% slower)
query_get_or_create 0.0013090134 0.0013397813 -0.0000307679 (2.35% slower)
l10n_render 0.0063407421 0.0063731670 -0.0000324249 (0.51% slower)
query_exists 0.0009287477 0.0009753466 -0.0000465989 (5.02% slower)
qs_filter_chaining 0.0008348227 0.0009381652 -0.0001033425 (12.38% slower)
query_raw_deferred 0.0182870388 0.0186469555 -0.0003599167 (1.97% slower)
model_save_new 0.0142104745 0.0145826817 -0.0003722072 (2.62% slower)
model_save_existing 0.0140855193 0.0145498991 -0.0004643798 (3.30% slower)
query_get 0.0273179531 0.0280359745 -0.0007180214 (2.63% slower)

Sum of all benchmarks (bigger is better): 0.0212707281

PirosB3 added some commits
@PirosB3 PirosB3 refactored test structure for DataTests.test_fields deab1ae
@PirosB3 PirosB3 implemented local_fields adfec5f
@PirosB3 PirosB3 finished refactoring local_concrete_fields 34a0c0f
@PirosB3 PirosB3 finished refactoring M2M tests d41fc3f
@PirosB3 PirosB3 refactored hidden RelatedObjects tests 0f27542
@PirosB3 PirosB3 finished tests suite refactor for related objects 51e8076
@PirosB3 PirosB3 added m2m tests 25e0857
@PirosB3 PirosB3 added virtual fields tests 8f72d94
@PirosB3 PirosB3 added AbstractPerson virtual fields tests 972e928
@PirosB3 PirosB3 re-ordered test results and removed debug functions a771555
@PirosB3 PirosB3 Added Relating and Related model to tests 57fb1f2
@PirosB3 PirosB3 finished adding relating and relation caa155f
@PirosB3 PirosB3 renamed test_legacy to test 06b98a3
@PirosB3 PirosB3 entire test suite refactor 2dc6b07
@PirosB3 PirosB3 removed debugging procedures e5e49e2
@PirosB3 PirosB3 renamed k to result_key c46fc26
@PirosB3 PirosB3 renamed model_options to model_meta cb1a4c3
@PirosB3 PirosB3 Merge branch 'master' of https://github.com/django/django into soc201…
…4_meta_unittests
cc0de86
@PirosB3 PirosB3 modified indentation of models 350ffb7
@PirosB3 PirosB3 refactored assertEqual and assertIn fdd84e3
@PirosB3 PirosB3 changed assertion isinstance to assertIsInstance d164acb
@PirosB3 PirosB3 moved test results one name for each line b38ee5a
@PirosB3 PirosB3 removed OrderedDict 045f1e6
@PirosB3 PirosB3 reinforced test asserting all local fields are bounded to the queries
model
632b0aa
@PirosB3 PirosB3 reinforced local data test f09c136
@PirosB3 PirosB3 improved styling and resolved unused imports 3a6032c
@PirosB3 PirosB3 removed unused helper functions 4f8015c
@PirosB3 PirosB3 removed unicode line for python 3 bd806bd
@PirosB3 PirosB3 fixed 3 python3 errors due to random dict ordering 786a423
@PirosB3 PirosB3 fixed last python 3 failing test
c19e543
@PirosB3 PirosB3 added new end of list style a9a55ef
@PirosB3 PirosB3 modified helper function to be all on one line 7ad8262
@PirosB3 PirosB3 Merge branch 'soc2014_meta_unittests' of http://github.com/PirosB3/dj…
…ango into soc2014_meta_refactor
6160221
@PirosB3 PirosB3 reconstructed test suite based on old test_refactored script 47c8bfc
@PirosB3 PirosB3 refactored data and m2m tests 397db52
@PirosB3 PirosB3 implemented all get_new_fields test f5f2b44
@PirosB3 PirosB3 removed redundancy and refactored get_new_fields 32bd898
@PirosB3 PirosB3 re-implemented get_new_field test 94d4762
@PirosB3 PirosB3 optimizing funciton calls a92c37f
@funkybob

Shouldn't this be using six.iteritems() ?

Also, is this measurably faster than a simple dict comprehension? It's certainly less readable :)

Further, this dict comprehension avoids the need for a lambda [and thus a stack frame] as well as the call to map... worth trying.

return {
    field: fn
    for fn, field in six.iteritems(self.get_new_fields(types=types, recursive=True))
}
PirosB3 added some commits
@PirosB3 PirosB3 added fields, local_fields, local_concrete_fields, and m2m fields as …
…get_new_fields
00a0414
@PirosB3 PirosB3 added related m2m endpoint 4f555ce
@PirosB3 PirosB3 added relatd m2m included hidden 419958a
@PirosB3 PirosB3 added related with model implementation 0dce3d6
@PirosB3 PirosB3 added m2m with model, added related objects with model d70f8d7
@PirosB3 PirosB3 found bug and fixed concete models issues 0aa6674
@PirosB3 PirosB3 implemebted get_field_with_models 6b671bf
@PirosB3 PirosB3 refactored get_field_by_name a114fa2
@PirosB3 PirosB3 added get_field_names, skipped some tests that rely on dict order f3138ea
@PirosB3 PirosB3 fixed an issue related to name ordering de7cad1
@PirosB3 PirosB3 reordered implementation of get_new_fields 70fc7d1
@PirosB3 PirosB3 temporarily removed caching daa48b0
@PirosB3 PirosB3 reinabled caching c71c06f
@PirosB3 PirosB3 made entire test suite passing on new API 2199fc1
@PirosB3 PirosB3 added conditional cached property c1e7f38
@PirosB3 PirosB3 added swapped models as cached properties 67ba3b1
@PirosB3 PirosB3 added LRU cache to field map ebdb5a1
@PirosB3 PirosB3 created ALL aggregate type 8dc041f
@PirosB3 PirosB3 added cached property on has_class_relation 3bde9dc
@PirosB3 PirosB3 removed lru_cache 17aea44
@PirosB3 PirosB3 fixed all failing tests but one 9620559
@PirosB3 PirosB3 fixed typo 5248a61
@PirosB3 PirosB3 commented out cached property currently dbef2cf
@PirosB3 PirosB3 removed kwargs from get_new_fields a55085b
@PirosB3 PirosB3 committed experiment 8999a97
@PirosB3 PirosB3 temporarily removed caching because of app migration renaming d91658a
@PirosB3 PirosB3 expire cache on each test e2e9d6f
@PirosB3 PirosB3 re-added property instead of cached-property ae19dfd
@PirosB3 PirosB3 removed debugger statements and added internal caching 69b292a
@PirosB3 PirosB3 re-added conditionl caching 0fe347c
@PirosB3 PirosB3 removed comments b74fdf4
@PirosB3 PirosB3 commented out debug code 0cbbd9a
@PirosB3 PirosB3 removed unused calls and debugger 0cccd63
@PirosB3 PirosB3 replaced get_all_field_names with new API 9b8e701
@PirosB3 PirosB3 replaced get_all_related_many_to_many_objects with new API c13feef
@PirosB3 PirosB3 replaced get_all_related_objects with new API d324317
@PirosB3 PirosB3 refactored get_concrete_fields_with_model with new API 89036d4
@PirosB3 PirosB3 fixed small bug in model class relation 5ce419f
@PirosB3 PirosB3 moved get_fields_with_model to the new API b86d49b
@PirosB3 PirosB3 moved local_concrete_fields to new API 04875ad
@PirosB3 PirosB3 refactored get_field_map into a cached function 1e08d81
@PirosB3 PirosB3 refactored internals to reflect new field changes a7d7f6a
@PirosB3 PirosB3 replaced first batch of get_field with get_new_field 601c48f
@PirosB3 PirosB3 fixed small typo 3f7e21a
@PirosB3 PirosB3 re-iterated over new API 7120965
@PirosB3 PirosB3 integrated new API 2bf1abe
@PirosB3 PirosB3 simplified get_field_by_name implementation efdda7f
@PirosB3 PirosB3 moved get_field to new API d241624
@PirosB3 PirosB3 refactored get_field_by_name to new API 429b510
@PirosB3 PirosB3 added second batch of get_field_by_name 5932953
@PirosB3 PirosB3 added cached properties 054ee36
@PirosB3 PirosB3 added final batch of get_field_by_name 87d3dde
@PirosB3 PirosB3 added cache expiry in different function c30ec83
@PirosB3 PirosB3 resolved small typo issue befcbd3
@PirosB3 PirosB3 refactored get_new_field b537a9c
@PirosB3 PirosB3 refactored internals into cached properties 4bb3a46
@PirosB3 PirosB3 small fixes
Conflicts:
	django/db/models/options.py
27ceb3f
@PirosB3 PirosB3 added final fix 1c3fbce
@PirosB3 PirosB3 added six iteritems and refactored related field method 354a7b7
@PirosB3 PirosB3 converting get_new_fields to cahced properties bcba146
@timgraham timgraham commented on an outdated diff
django/db/models/options.py
@@ -23,6 +31,27 @@
'select_on_save', 'default_related_name')
+@lru_cache(maxsize=None)
+def _map_model(opts, connection):
+ direct = isinstance(connection, Field) or hasattr(connection, 'for_concrete_model')
+ model = connection.model if direct else connection.parent_model._meta.concrete_model
+ if model == opts.model:
+ model = None
+ return connection, model
+
+
+@lru_cache(maxsize=None)
+def _map_details(opts, connection):
@timgraham Django member

_map_model_details?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/db/models/options.py
@@ -45,9 +74,35 @@ def normalize_together(option_together):
return option_together
+class raise_deprecation(object):
+
+ def __init__(self, suggested_alternative):
+ self.suggested_alternative = suggested_alternative
+
+ def __call__(self, fn):
+
+ def wrapper(*args, **kwargs):
+ warnings.warn(
+ "'%s is an unofficial API that has been deprecated. "
+ "Usage of %s may be able to be replaced with "
@timgraham Django member

I'd say "You may be able to replace it with ..." to avoid including the function name twice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/db/models/options.py
@@ -100,6 +155,10 @@ def __init__(self, meta, app_label=None):
self.apps = apps
self.default_related_name = None
+ self._map_model = partial(_map_model, self)
+ self._map_details = partial(_map_details, self)
+
+ ### INTERNAL METHODS AND PROPERTIES GO BELOW THIS LINE ###
@timgraham Django member

I would rather not do this reordering as part of this diff. It makes things quite a bit scarier, even though I believe most of the code has simply been moved. If you want to submit a patch to do this for the existing methods, we could merge it first and then rebase this patch on top of it, but I'm not sure it's worth it (to reorder at all).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/db/models/options.py
((75 lines not shown))
"""
- return self.concrete_fields.index(self.pk)
+ # NOTE: previous get_field API had a many_to_many key. This key
+ # has now become m2m. In order to avoid breaking other's implementation
+ # we will catch the use of 'many_to_many' key and convert it to m2m.
+ try:
+ m2m = kwargs['many_to_many']
@timgraham Django member

to minimize size of try:

try:
   m2m = kwargs['many_to_many']
except KeyError:
   pass
else:
   warnings.warn(....
@PirosB3
PirosB3 added a note

@timgraham done
Just for interest, what are the benefits of [try / catch / else] compared to [try / catch] ?

Thanks

@timgraham Django member

Keeping the try block limited to just the code you expect to throw the exception is a good practice. It prevents a situation where there's some other bug than what you expected. For example, if warnings.warn( somehow threw a KeyError and it was in the try block, you would unexpectedly hide that bug.

@PirosB3
PirosB3 added a note

Understood! thanks for the explanation. This has been changed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/db/models/options.py
((90 lines not shown))
- def setup_proxy(self, target):
+ # Creates a cache key composed of all arguments
+ cache_key = (m2m, data, related_objects, related_m2m, virtual,)
@timgraham Django member

omit trailing , before ) (only include it if the tuple is multilined)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/db/models/options.py
((147 lines not shown))
- def __str__(self):
- return "%s.%s" % (smart_text(self.app_label), smart_text(self.model_name))
+ try:
+ # In order to avoid list manipulation. Always
+ # return a shallow copy of the results
+ return copy(self._get_fields_cache[cache_key])
+ except KeyError:
+ pass
+
+ # Using an OrderedDict to preserve the order of insertion. This is fundamental
+ # when displaying a ModelForm or django.contrib.admin panel and no specific ordering
+ # is provided. For this reason, order of field insertion must be preserved
+ fields = OrderedDict()
+ options = {'include_parents': include_parents,
@timgraham Django member

The style I prefer is

options = {
    'include_parents': include_parents,
    ....
}

It's somewhat of a pain to indent additional items if your editor doesn't do it automatically with the other style.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/db/models/options.py
((190 lines not shown))
+ parent_list = self.get_parent_list()
+ # Recursively call get_fields on each parent, with the same options provided
+ # in this call
+ for parent in self.parents:
+ for obj, query_name in six.iteritems(parent._meta.get_fields(data=False, related_objects=True,
+ **dict(options, include_hidden=True))):
+ if not ((obj.field.creation_counter < 0
+ or obj.field.rel.parent_link)
+ and obj.model not in parent_list):
+ if include_hidden or not obj.field.rel.is_hidden():
+ # If hidden fields should be included or the relation
+ # is not intentionally hidden, add to the fields dict
+ fields[obj] = query_name
+
+ # Tree is computer once and cached until apps cache is expired. It is composed of
+ # { options_instance : [field_pointing_to_options_model, field_pointing_to_options, ..]}
@timgraham Django member

pep8: no spaces around options_instance

@PirosB3
PirosB3 added a note

done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/db/models/options.py
((386 lines not shown))
- if model:
- cache.append((field, model))
- else:
- cache.append((field, parent))
- cache.extend((f, None) for f in self.local_fields)
- self._field_cache = tuple(cache)
- self._field_name_cache = [x for x, _ in cache]
-
- def _many_to_many(self):
- try:
- self._m2m_cache
- except AttributeError:
- self._fill_m2m_cache()
- return list(self._m2m_cache)
- many_to_many = property(_many_to_many)
+ @raise_deprecation(suggested_alternative="get_fields")
@timgraham Django member

get_fields() (add parenthesis) to distinguish it from other alternatives which are properties

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/db/models/options.py
((562 lines not shown))
- if (hasattr(f, 'rel') and f.rel and not isinstance(f.rel.to, six.string_types)
- and f.generate_reverse_relation):
- if self == f.rel.to._meta:
- cache[f.related] = None
- proxy_cache[f.related] = None
- elif self.concrete_model == f.rel.to._meta.concrete_model:
- proxy_cache[f.related] = None
- self._related_objects_cache = cache
- self._related_objects_proxy_cache = proxy_cache
-
+ include_parents = local_only is False
+ fields = self.get_fields(
+ data=False, related_objects=True,
+ include_parents=include_parents,
+ include_hidden=include_hidden,
+ include_proxy=include_proxy_eq
@timgraham Django member

include trailing ,

@PirosB3
PirosB3 added a note

done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/db/models/sql/compiler.py
@@ -633,10 +636,14 @@ def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
else:
restricted = False
- for f, model in opts.get_fields_with_model():
+ for f in opts.fields:
+ # TODO: deprecated
@timgraham Django member

still TODO? not clear to me what's deprecated here.

@PirosB3
PirosB3 added a note

refactored

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/internals/deprecation.txt
@@ -40,6 +40,19 @@ about each item can often be found in the release notes of two versions prior.
* ``django.template.resolve_variable`` will be removed.
+* The following methods will be removed from :class:`django.db.models.options` in
+ favour of the new formal Options API:
@timgraham Django member

favor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
@@ -0,0 +1,277 @@
+=======================
+Model ``_meta`` options
+=======================
+
+.. module:: django.db.models.options
+ :synopsis: Model meta-class layer
+
+The ``Options API`` is at the core of Django, it enables other parts of the
@timgraham Django member

I wouldn't use `` here, or at least not include API in it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
@@ -0,0 +1,277 @@
+=======================
+Model ``_meta`` options
+=======================
+
+.. module:: django.db.models.options
+ :synopsis: Model meta-class layer
+
+The ``Options API`` is at the core of Django, it enables other parts of the
+system such as lookups, queries, forms and the admin to understand the
@timgraham Django member

comma after forms

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on the diff
docs/ref/models/meta.txt
@@ -0,0 +1,277 @@
+=======================
@timgraham Django member

Please review line breaks in this doc and break as close to 80 characters as practically possible

@PirosB3
PirosB3 added a note

done. is this okay now?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
@@ -0,0 +1,277 @@
+=======================
+Model ``_meta`` options
+=======================
+
+.. module:: django.db.models.options
+ :synopsis: Model meta-class layer
+
+The ``Options API`` is at the core of Django, it enables other parts of the
+system such as lookups, queries, forms and the admin to understand the
+capabilities of every model. The ``Options API`` is
+hidden under the _meta attribute of each model class.
@timgraham Django member

add `` for _meta

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
@@ -0,0 +1,277 @@
+=======================
+Model ``_meta`` options
+=======================
+
+.. module:: django.db.models.options
+ :synopsis: Model meta-class layer
+
+The ``Options API`` is at the core of Django, it enables other parts of the
+system such as lookups, queries, forms and the admin to understand the
+capabilities of every model. The ``Options API`` is
+hidden under the _meta attribute of each model class.
+
+Options can be used for the following:
@timgraham Django member

remove "the following"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
((7 lines not shown))
+
+The ``Options API`` is at the core of Django, it enables other parts of the
+system such as lookups, queries, forms and the admin to understand the
+capabilities of every model. The ``Options API`` is
+hidden under the _meta attribute of each model class.
+
+Options can be used for the following:
+
+* Retrieving all field names of a model
+* Retrieving all field instances names of a model
+* Retrieving a single field by name
+
+Field Types
+===========
+
+A model can have 5 types of fields:
@timgraham Django member

5 -> several (the number isn't important and we can avoid haven't to update this if we add more types in the future)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
((13 lines not shown))
+Options can be used for the following:
+
+* Retrieving all field names of a model
+* Retrieving all field instances names of a model
+* Retrieving a single field by name
+
+Field Types
+===========
+
+A model can have 5 types of fields:
+
+Data fields
+~~~~~~~~~~~
+
+A data field is any field that has an entry on the database,
+for example a CharField, BooleanField, a ForeignKey.
@timgraham Django member

`` around class names (please review the entire doc for this)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
((18 lines not shown))
+
+Field Types
+===========
+
+A model can have 5 types of fields:
+
+Data fields
+~~~~~~~~~~~
+
+A data field is any field that has an entry on the database,
+for example a CharField, BooleanField, a ForeignKey.
+
+Many-to-many fields
+~~~~~~~~~~~~~~~~~~~
+
+A many-to-many field that is defined on the current model.
@timgraham Django member

not a complete sentence

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on the diff
docs/ref/models/meta.txt
((35 lines not shown))
+Related Object
+~~~~~~~~~~~~~~
+
+A Related Object is a one-to-many relation from another model
+(such as a :class:`django.db.models.ForeignKey`) that points
+to the current model.
+
+.. code-block:: python
+
+ class City(models.Model):
+ name = models.CharField(max_length=100)
+
+ class Person(models.Model):
+ city = models.ForeignKey(City)
+
+In this case, ``City`` has a related object from ``Person``
@timgraham Django member

missing period (please review entire doc)
"City has a related object from Person" -- so this is about reverse relations then? This could be clarified.
Also why is it called "object" when the previous headings are called "fields".

@PirosB3
PirosB3 added a note

@timgraham added quotes now.
I think the whole terminology should be discussed. I will be posing more information on the ml and on the wiki page

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
@@ -0,0 +1,277 @@
+=======================
+Model ``_meta`` options
+=======================
+
+.. module:: django.db.models.options
+ :synopsis: Model meta-class layer
+
+The ``Options API`` is at the core of Django, it enables other parts of the
+system such as lookups, queries, forms and the admin to understand the
+capabilities of every model. The ``Options API`` is
+hidden under the _meta attribute of each model class.
+
@timgraham Django member

Should have .. versionadded:: 1.8 around here to indicate that most of this was added and/or formalized in 1.8.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
((111 lines not shown))
+~~~~~~
+
+Hidden fields are only referred to related objects and related many-to-many.
+When a relational model (such as :class:`django.db.models.ManyToManyField`,
+or :class:`django.db.models.ForeignKey`) specifies a ``related_name`` that
+starts with a "+", it tells Django to not create a reverse relation.
+
+.. code-block:: python
+
+ class City(models.Model):
+ name = models.CharField(max_length=100)
+
+ class Person(models.Model):
+ city = models.ForeignKey(City, related_name='+')
+
+``City`` has a related hidden object from ``Person`` (as you can't access person_set)
@timgraham Django member

`` around person_set

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
((93 lines not shown))
+Local
+~~~~~
+
+A local field is one that's declared directly on a model and not derived from inheritance.
+Fields from models that directly inherit from abstract models or proxy classes
+are still local.
+
+.. code-block:: python
+
+ class Person(models.Model):
+ name = models.CharField(max_length=50)
+
+ class Londoner(Person):
+ overdraft = models.DecimalField()
+
+``Londoner`` has two fields (name and overdraft) but only one local field (overdraft).
@timgraham Django member

`` around field names

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
((56 lines not shown))
+
+.. code-block:: python
+
+ class City(models.Model):
+ name = models.CharField(max_length=100)
+
+ class Person(models.Model):
+ cities_lived_in = models.ManyToManyField(City)
+
+In this case, City has a related many-to-many from Person
+
+Virtual fields
+~~~~~~~~~~~~~~
+
+Virtual fields are model fields that do not necessarily have a
+corresponding database column. they are "model fields" such as
@timgraham Django member

not sure "model fields" needs quotes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
((153 lines not shown))
+``Options API``
+===============
+
+Retrieve a field instance by name
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. method:: Options.get_field(field_name, m2m=True, data=True, related_m2m=False, related_objects=False, virtual=False)
+
+ Returns the field instance given a name of a field.
+
+ ``field_name`` can be the name of a field on a model or it can also be a reverse relation
+ name (related query name).
+
+ ``field_name`` is the only **required** field. All the other arguments
+ are flags that are used internally to optimize the lookup. By default, :meth:`Options.get_field()`
+ will only look up data and many-to-many fields, but all field types can be activated.
@timgraham Django member

activated -> searched/queried?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
((155 lines not shown))
+
+Retrieve a field instance by name
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. method:: Options.get_field(field_name, m2m=True, data=True, related_m2m=False, related_objects=False, virtual=False)
+
+ Returns the field instance given a name of a field.
+
+ ``field_name`` can be the name of a field on a model or it can also be a reverse relation
+ name (related query name).
+
+ ``field_name`` is the only **required** field. All the other arguments
+ are flags that are used internally to optimize the lookup. By default, :meth:`Options.get_field()`
+ will only look up data and many-to-many fields, but all field types can be activated.
+ Hidden and proxied relations are not available. If a field with the given name is not found
+ it will raise a `FieldDoesNotExist`` error.
@timgraham Django member

error -> exception

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
((161 lines not shown))
+ Returns the field instance given a name of a field.
+
+ ``field_name`` can be the name of a field on a model or it can also be a reverse relation
+ name (related query name).
+
+ ``field_name`` is the only **required** field. All the other arguments
+ are flags that are used internally to optimize the lookup. By default, :meth:`Options.get_field()`
+ will only look up data and many-to-many fields, but all field types can be activated.
+ Hidden and proxied relations are not available. If a field with the given name is not found
+ it will raise a `FieldDoesNotExist`` error.
+
+ .. code-block:: python
+
+ >>> from django.contrib.auth.models import User
+
+ >>> User._meta.get_field('username')# A data field
@timgraham Django member

pep8: two spaces before inline comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
((181 lines not shown))
+
+ >>> LogEntry._meta.get_field('user') # ForeignKey can be queried by field name
+ <django.db.models.fields.related.ForeignKey: user>
+ >>> LogEntry._meta.get_field('user_id') # .. and also by database column name
+ <django.db.models.fields.related.ForeignKey: user>
+
+ >>> User._meta.get_field('does_not_exist') # A non existent field
+ Traceback (most recent call last):
+ ...
+ FieldDoesNotExist: User has no field named 'does_not_exist'
+
+ .. note::
+ The default arguments of :meth:`Options.get_field()` might not be what you expect.
+ :meth:`Options.get_fields()` only enables data fields, while :meth:`Options.get_field()`
+ enables data and m2m. This is because of backwards-compatibility
+ issues (read more below).
@timgraham Django member

link to specific section where I can read more

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
((206 lines not shown))
+ .. code-block:: python
+
+ >>> User._meta.field_names
+ ['username', 'first_name', 'last_name', 'logentry',
+ 'is_active', 'email', 'is_superuser', 'is_staff',
+ 'last_login', 'groups', 'user_permissions', 'password',
+ 'id', 'date_joined']
+
+Retrieve field instances
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. method:: Options.get_fields(m2m=False, data=True, related_m2m=False, related_objects=False, virtual=False, include_parents=True, include_non_concrete=True, include_hidden=False, include_proxy=False)
+
+ Returns a list of fields given a list of field types and filters.
+
+ get_fields takes a set of flags as parameters and returns a tuple
@timgraham Django member

get_fields()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
((210 lines not shown))
+ 'is_active', 'email', 'is_superuser', 'is_staff',
+ 'last_login', 'groups', 'user_permissions', 'password',
+ 'id', 'date_joined']
+
+Retrieve field instances
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. method:: Options.get_fields(m2m=False, data=True, related_m2m=False, related_objects=False, virtual=False, include_parents=True, include_non_concrete=True, include_hidden=False, include_proxy=False)
+
+ Returns a list of fields given a list of field types and filters.
+
+ get_fields takes a set of flags as parameters and returns a tuple
+ of field instances. The flags are a combination of field types
+ and field filters. All possible combinations of filters and fields
+ are possible, although some will have no effect (such as
+ include_proxy combined with data or m2m by itself).
@timgraham Django member

`` around include_proxy, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
((244 lines not shown))
+
+ >>> User._meta.get_fields(data=False, related_objects=True, include_hidden=True) # only related_objects including hidden
+ (<RelatedObject: auth:user_groups related to user>,
+ <RelatedObject: auth:user_user_permissions related to user>,
+ <RelatedObject: admin:logentry related to user>)
+
+``Fast-Access API``
+===================
+
+In order to provide a simple and usable API for developers to use,
+Django provides some Fast-access endpoints that are used
+by Django internally. The reason these APIs exist is to ensure
+field access is always fast. This API is composed of a specific set
+of properties that are cached on first access, and are guaranteed to
+be fast. If you have the choice between using a fast-access API
+or using one of the official APIs, it is recommended to use the
@timgraham Django member

don't think there's a need to distinguish these from "official APIs" -- if they are documented, they are official APIs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/ref/models/meta.txt
((245 lines not shown))
+ >>> User._meta.get_fields(data=False, related_objects=True, include_hidden=True) # only related_objects including hidden
+ (<RelatedObject: auth:user_groups related to user>,
+ <RelatedObject: auth:user_user_permissions related to user>,
+ <RelatedObject: admin:logentry related to user>)
+
+``Fast-Access API``
+===================
+
+In order to provide a simple and usable API for developers to use,
+Django provides some Fast-access endpoints that are used
+by Django internally. The reason these APIs exist is to ensure
+field access is always fast. This API is composed of a specific set
+of properties that are cached on first access, and are guaranteed to
+be fast. If you have the choice between using a fast-access API
+or using one of the official APIs, it is recommended to use the
+fast-access API for performance reasons
@timgraham Django member

missing period

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/releases/1.8.txt
@@ -184,6 +184,19 @@ Management Commands
Models
^^^^^^
+* Added the new Model._meta API in the :class:`~django.db.models.options.Options`
@timgraham Django member

this can be listed as a "major feature" above the "minor features" section. See 1.7 release notes for an example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/releases/1.8.txt
@@ -184,6 +184,19 @@ Management Commands
Models
^^^^^^
+* Added the new Model._meta API in the :class:`~django.db.models.options.Options`
+ class. The API is formally maintained, documented and can be used for public use.
+ The new API is componsed of 2 main endpoints:
+ :meth:`Options.get_fields() <django.db.models.options.Options.get_fields()>`
+ and :meth:`Options.get_field() <django.db.models.options.Options.get_field()>`.
+ The new API contains several fast-access cached properties that are documented
+ in https://code.djangoproject.com/wiki/new_meta_api.
@timgraham Django member

we shouldn't use the wiki as official documentation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/releases/1.8.txt
@@ -184,6 +184,19 @@ Management Commands
Models
^^^^^^
+* Added the new Model._meta API in the :class:`~django.db.models.options.Options`
+ class. The API is formally maintained, documented and can be used for public use.
+ The new API is componsed of 2 main endpoints:
+ :meth:`Options.get_fields() <django.db.models.options.Options.get_fields()>`
+ and :meth:`Options.get_field() <django.db.models.options.Options.get_field()>`.
+ The new API contains several fast-access cached properties that are documented
+ in https://code.djangoproject.com/wiki/new_meta_api.
+
+* A series of methods in :class:`~django.db.models.options.Options` have been
@timgraham Django member

this goes under the deprecation section of the release notes

@PirosB3
PirosB3 added a note

Done.

@timgraham often sphinx compiler complains about:
1.8.txt:26: WARNING: py:class reference target not found: django.db.models.options.Options

Trying to solve this but cannot understand why it can't find the class. Any ideas?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on the diff
docs/ref/models/meta.txt
@@ -0,0 +1,277 @@
+=======================
+Model ``_meta`` options
+=======================
+
+.. module:: django.db.models.options
+ :synopsis: Model meta-class layer
+
@timgraham Django member

Add .. class:: Options around here to fix those sphinx errors

@PirosB3
PirosB3 added a note

done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@PirosB3

@timgraham for convenience I deployed all my documentation to a server, so it can be easier to review the style.

All this PRs documentation will be deployed here: http://5.101.98.142:49155
ex: http://5.101.98.142:49155/ref/models/meta.html

@PirosB3

@timgraham @freakboy3742 can we have another review?

@timgraham: I will be reverting the structure of the options file (discarding the way I ordered it)

@Ian-Foote Ian-Foote commented on an outdated diff
django/db/models/options.py
@@ -23,6 +32,32 @@
'select_on_save', 'default_related_name')
+IMMUTABLE_WARNING = "get_fields return type should never be mutated. If you want \
+to manipulate get_fields return type should never be mutated. If you want to \
+manipulate this list for your own use, make a copy first"

Did you mean:

IMMUTABLE_WARNING = "get_fields return type should never be mutated. If you want \
to manipulate this list for your own use, make a copy first"
@PirosB3
PirosB3 added a note

aha! Thanks @Ian-Foote will change now!

@timgraham Django member

preferred style is not to use backslashes, e.g.

FOO = (
    "..."
    "..."
)

Also please always add () when referring to methods (get_fields() in this case) in docs or messages to distinguish them from attributes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/releases/1.8.txt
@@ -23,7 +23,13 @@ Like Django 1.7, Django 1.8 requires Python 2.7 or above, though we
What's new in Django 1.8
========================
-...
+* Added the new Model._meta API in the :class:`~django.db.models.options.Options`
@timgraham Django member

please add a heading rather than using a bulleted list for major features as in 1.7 release notes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/releases/1.8.txt
@@ -428,6 +435,13 @@ Miscellaneous
Features deprecated in 1.8
==========================
+``django.db.models.options.Options``
@timgraham Django member

Selected methods in django.db.models.options.Options

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/releases/1.8.txt
@@ -428,6 +435,13 @@ Miscellaneous
Features deprecated in 1.8
==========================
+``django.db.models.options.Options``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@timgraham Django member

fix underline length and add newline after

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
docs/releases/1.8.txt
@@ -428,6 +435,13 @@ Miscellaneous
Features deprecated in 1.8
==========================
+``django.db.models.options.Options``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A series of methods in :class:`~django.db.models.options.Options` have been
+deprecated in favor of newer ones. Each deprecated method is specified in
+:ref:`the deprecation timeline <deprecation-removed-in-2.0>`, it will be backwards-compatible till
@timgraham Django member

Please list the methods here.
"They will be removed in Django 2.0." (don't need to mention backwards compatible/raise warning as that's the case for all deprecated features)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
PirosB3 added some commits
@PirosB3 PirosB3 did documentation fixes and indentation fixes f28d0b0
@PirosB3 PirosB3 Merge branch 'master' of https://github.com/django/django into soc201…
…4_meta_refactor_upgrade_flags_get_field

Conflicts:
	django/db/migrations/operations/models.py
	docs/internals/deprecation.txt
dfb9ac7
@PirosB3 PirosB3 small documentation fix c15489f
@timgraham timgraham commented on an outdated diff
django/apps/registry.py
@@ -323,12 +323,30 @@ def unset_installed_apps(self):
self.apps_ready = self.models_ready = self.ready = True
self.clear_cache()
+ def clear_model_relation_tree(self):
+ """
+ Clears the pre-generated relation tree for all models. This should
+ be used in tests or when a new model is added.
+ """
+ if not self.ready:
+ raise AppRegistryNotReady("App registry isn't ready yet. relation tree \
@timgraham Django member

don't use backslash for line continuation, see the style used in the rest of this file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/contrib/admin/options.py
@@ -401,7 +401,7 @@ def lookup_allowed(self, lookup, value):
rel_name = None
for part in parts[:-1]:
try:
- field, _, _, _ = model._meta.get_field_by_name(part)
+ field = model._meta.get_field(part, related_objects=True, related_m2m=True, virtual=True)
@timgraham Django member

You removed virtual=True from the one place I commented, but not everywhere else you used the same pattern. Anyway, did you decide about using a shortcut function?

@PirosB3
PirosB3 added a note

OK, so...
@timgraham @freakboy3742

There are 3 used combinations of get_field() in the entire Django codebase

get_field(data=True, m2m=True) > 60% of the occurrences
get_field(data=True, m2m=True, related_objects=True, related_m2m=True, virtual=True) > 39.9% of the occurrences
get_field(data=False, m2m=True) once only! (0.01% of occurrences).

My proposal

go back to the old get_field(field_name, include_related=False) and handle the occurance that happens only once indipendently.
Benefits:

  • better and simpler API
  • better memory management (caching)

What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on the diff
django/contrib/gis/utils/srs.py
@@ -62,10 +62,9 @@ def add_srs_entry(srs, auth_name='EPSG', auth_srid=None, ref_sys_name=None,
}
# Backend-specific fields for the SpatialRefSys model.
- srs_field_names = SpatialRefSys._meta.get_all_field_names()
@timgraham Django member

I would have kept the srs_field_names variable.

@PirosB3
PirosB3 added a note

done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/db/models/deletion.py
@@ -172,7 +183,7 @@ def collect(self, objs, source=None, nullable=False, collect_related=True,
model = new_objs[0].__class__
# Recursively collect concrete model's parent models, but not their
- # related objects. These will be found by meta.get_all_related_objects()
+ # related objects. These will be found by meta.get_new_fields
@timgraham Django member

get_new_fields isn't a thing I think

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on the diff
django/contrib/contenttypes/fields.py
@@ -30,9 +30,14 @@ def __init__(self, ct_field="content_type", fk_field="object_id", for_concrete_m
def contribute_to_class(self, cls, name, **kwargs):
self.name = name
+
+ # Set model to cls (as it is not associated to anything)
@timgraham Django member

This comment doesn't really do anything to clarify things for me (and I see the same pattern elsewhere uncommented).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/db/models/options.py
((660 lines not shown))
- model, which points, eventually, to the ancestor). Used when
- constructing table joins for model inheritance.
-
- Returns None if the model isn't an ancestor of this one.
- """
- if ancestor in self.parents:
- return self.parents[ancestor]
- for parent in self.parents:
- # Tries to get a link field from the immediate parent
- parent_link = parent._meta.get_ancestor_link(ancestor)
- if parent_link:
- # In case of a proxied model, the first link
- # of the chain to the ancestor is that parent
- # links
- return self.parents[parent] or parent_link
+ return list(map(self._map_model, self.get_fields(data=False, related_m2m=True)))
@timgraham Django member

I would use a list comprehension rather than list(map())

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/db/models/options.py
((565 lines not shown))
- for klass in self.apps.get_models(include_auto_created=True):
- if not klass._meta.swapped:
- for f in klass._meta.local_fields + klass._meta.virtual_fields:
- if (hasattr(f, 'rel') and f.rel and not isinstance(f.rel.to, six.string_types)
- and f.generate_reverse_relation):
- if self == f.rel.to._meta:
- cache[f.related] = None
- proxy_cache[f.related] = None
- elif self.concrete_model == f.rel.to._meta.concrete_model:
- proxy_cache[f.related] = None
- self._related_objects_cache = cache
- self._related_objects_proxy_cache = proxy_cache
+ return map(self._map_model, self.get_all_related_objects(
+ local_only=local_only,
+ include_hidden=include_hidden,
+ include_proxy_eq=include_proxy_eq
@timgraham Django member

add trailing comma

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/db/models/options.py
((447 lines not shown))
- with it).
-
- Uses a cache internally, so after the first access, this is very fast.
- """
- try:
- try:
- return self._name_map[name]
- except AttributeError:
- cache = self.init_name_map()
- return cache[name]
- except KeyError:
- raise FieldDoesNotExist('%s has no field named %r'
- % (self.object_name, name))
+ return self._map_model_details(self.get_field(
+ name, m2m=True, related_objects=True,
+ related_m2m=True, virtual=True
@timgraham Django member

add trailing comma

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/db/models/options.py
((343 lines not shown))
@cached_property
def concrete_fields(self):
+ """
+ Returns a list of all concrete data fields on the model and it's parents.
@timgraham Django member

its (In case it's unclear, "it's" means "it is" which doesn't make sense). Please check all the docstrings so I don't have to comment every place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/db/models/options.py
@@ -22,6 +31,32 @@
'index_together', 'apps', 'default_permissions',
'select_on_save', 'default_related_name')
+IMMUTABLE_WARNING = (
+ "get_fields() return type should never be mutated. If you want "
@timgraham Django member

The return type of get_field()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/db/models/options.py
@@ -22,6 +31,32 @@
'index_together', 'apps', 'default_permissions',
'select_on_save', 'default_related_name')
+IMMUTABLE_WARNING = (
+ "get_fields() return type should never be mutated. If you want "
+ "to manipulate this list for your own use, make a copy first"
@timgraham Django member

add period

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/db/models/options.py
@@ -22,6 +31,32 @@
'index_together', 'apps', 'default_permissions',
'select_on_save', 'default_related_name')
+IMMUTABLE_WARNING = (
+ "get_fields() return type should never be mutated. If you want "
+ "to manipulate this list for your own use, make a copy first"
+)
+
+
+@lru_cache(maxsize=None)
+def _map_model(opts, link):
@timgraham Django member

is link what we call rel in the rest of the code? link has me thinking <a href="...">. Naming is hard.

@PirosB3
PirosB3 added a note

link is the new name for (ex) connection

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on an outdated diff
django/db/models/options.py
@@ -110,6 +177,59 @@ def app_config(self):
def installed(self):
return self.app_config is not None
+ @property
+ def verbose_name_raw(self):
@timgraham Django member

Everything is still moved around in this file making review difficult.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@PirosB3

@timgraham old formatting of models/options has been restored.

@PirosB3

@timgraham @freakboy3742

This is the new get_field() API: PirosB3#6. Do you prefer this or the one on the current PR?
I prefer the new one, it's simpler and there is (not yet) any reason for so much filtering options. It also has smaller memory usage.
Suggestions?

@timgraham
Django member

buildbot, test this please.

@PirosB3
@timgraham timgraham closed this
@matthiask matthiask referenced this pull request in django-mptt/django-mptt
Closed

Add support for models where primary key is not Integer type #322

@tomchristie

Linking to newer ticket for folks browsing this... #3114.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.