On Wednesday, August 20, 2014 11:19:33 AM UTC+3, Russell Keith-Magee wrote: > > I think Daniel and I might have come up with a way to meet both these > requirements - a minimalist API for get_fields, with at least some > protection against the known incoming backwards compatibility issue. > > The summary so far: it appears that a complex taxonomy isn't especially > helpful - firstly, because any complex taxonomy is going to have edge cases > that are hard to categorize, but also because a complex taxonomy leads to a > much more complex internal API that is going to be prone to backwards > compatibility problems. > > So - instead of worrying about 'virtual' and other properties like that, > lets look at why the _meta API is fundamentally used - to get a list of > fields that need to be handled in data processing. This primarily means > forms, but other forms of serialisation are also included. In these use > cases, there are always going to be per-field differences (even a CharField > and an IntegerField require *slightly* different handling), so we won't > focus on internal representations, storage mechanisms, or anything like > that. Instead, lets focus on cardinality - a field represents some sort of > data that has a cardinality with the object on which it is stored. If > something has cardinality 1, you can display a single field. If it's > cardinality N, you need to display a list, or some sort of inline. > > This results in 3 categories that are mutually exclusive: > > a) "Data fields": Fields of cardinality 0-1: > <SNIP> > b) "ManyToMany Fields": Fields that are locally defined that represent a > cardinality 0-N relationship with another object: > <SNIP> > c) "Related Objects": Fields that represent a cardinality 0-N relationship > with this object, but aren't locally defined: > <SNIP> >
> These three types are mutually exclusive - you either have cardinality 1 > *or* cardinality N, not both; and you're either locally defined on this > object or you're not. I can't think of an example of "cardinality 1 data > that isn't defined on this object", but it would fit into this taxonomy if > it were needed; I also can't think of a field definition that would span > models. > The reverse of OneToOneField is a cardinality 1 data that isn't defined on this object. > > In addition to this basic classification, a field can be marked as > "hidden". The immediate use for this is to hide the related_name='+' case > of a FK or M2M. Looking forward, it would be used to mask fields that > exist, but aren't intended to be user visible - for example, in the > potential future case where a ForeignKey is split in two, or a Composite > Key, there would be a "hidden" integer field (or fields) storing the actual > data, and a virtual (but non-hidden) field that is the public API for > manipulating the relationship. This would also be backwards compatible, > because the "visible" field list hasn't changed. > There are use cases that do not fit this categorization. For example when instantiating a model from database you will need to supply the hidden integer field data for a foreign key, but you must skip the foreign key field itself. That is, a model with relation to author is initialized as MyModel(pk=1, author_first_name='foo', author_last_name='bar') (technically this is done through *args for performance reasons), not with MyModel(pk=1, author=author_instance). Similar considerations likely apply to serialization of models. Form fields for a model is another consideration. If one wants those fields that should have a field in a form, that is currently defined as [f for f in model._meta.fields if f.editable]. The editable fields set doesn't necessarily match the above categorization. In fact, I believe if we inspect Django's code base it will be clear there can't be any categorization where fields belong to only one category, but which fulfills all use cases in Django. It is like trying to categorize animals for every use case. If you want mammals, then categorization to sea and land creatures will not work. If you want sea creatures, then categorization to mammals and fish is useless. The point is that I am convinced we will need to provide field flags to complement the get_fields() API no matter what API we choose for get_fields(). In fact, if we define and document a sane set of field flags, then the get_fields() API isn't that important, it just needs to be useful for the most common use cases. > Fields are also tracked according to their parentage; this is used by > tools interacting with inheritance relationships to know which fields are > actually on this model, and which are inherited from a base class. > > This yields the following formal API for _meta: > > * get_fields(data, many_to_many, related, include_hidden, include_parents) > > * @property data_fields (=> get_fields(data=True, many_to_many=False, > related=False, include_hidden=False, include_parents=True) > > * @property many_to_many_fields (=> get_fields(data=False, > many_to_many=True, related=False, include_hidden=False, > include_parents=True) > > * @property related_objects (=> get_fields(data=False, > many_to_many=False, related=True, include_hidden=False, > include_parents=True) > > Does this sound any more sane as an API? > Yes, with the cave-eat that for example model initialization fields through *args do not map to this API, at least not after foreign key split to virtual field + concrete fields. Similar for editable fields. So, +1 if we also consider defining and documenting an useful set of field flags. I wonder if a better name for the related category exists. My first instinct is that foreign key fields should match the related flag. Could it be made cleaner that these are relations defined on remote model? Maybe just remote_relations could work? - Anssi -- You received this message because you are subscribed to the Google Groups "Django developers" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at http://groups.google.com/group/django-developers. To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/13052c7c-9a29-46e6-a90c-f01dfec46f97%40googlegroups.com. For more options, visit https://groups.google.com/d/optout.
