As most of you have probably noticed by now, in a week and a half I'll
start working on the implementation of composite fields. Before that
we should probably agree on the final form of the API.

This lengthy mail is mostly a recapitulation of things mentioned in
the past, like [1], [2], [3] and questions raised in these discussions
that have not been answered so far.


CompositeField will be a new type of model fields. This type will be
virtual, i. e. it won't be backed by any real database column by
itself. Instead, it will act as a proxy to a given set of atomic
fields and will be used to set options for and perform operations on
the whole set as one field.


The constructor of a CompositeField will require at least two
positional parameters, each positional parameter will be a single
atomic field. The order of this parameters will be important as
explained below. The parameters will have to be field instances, lazy
loading won't be necessary (the recommended place of composite field
definitions will be after atomic fields).

CompositeField will accept these three field options:
- db_index (creates a multi-column index across the underlying fields)
- primary_key (creates a composite primary key in the model)
- unique (creates a unique constraint for the set of fields)

Other field options either wouldn't make sense or would be too
difficult to implement.

There is a clash with the current API here, in the ``unique`` option.
This would supersede the current ``unique_together`` Meta option. I
see three options possible:

1) Leave out the ``unique`` option and live with ``unique_together``.
   This would pribably imply also leaving out ``db_index``, otherwise
   the API would be a complete mess.

2) Allow ``CompositeField.unique`` but also keep ``unique_together``.
   The problem I see with this approach is that there would be two
   quite different ways to achieve the same effect.

3) Make ``CompositeField.unique`` the way to go and deprecate
   ``unique_together``.
   This way, specifying a unique constraint on a tuple of fields would
   work the same way it works on single fields which is IMO a
   significant benefit. There's, however, the issue of breaking
   backwards compatibility. Furthermore, one would have to add a new
   field, albeit virtual, just to create a simple constraint, which
   may seem weird to some.

I don't feel like deciding in this and either one is fine as far as
the implementation is concerned.

One minor detail, should the field silently ignore invalid options or
should it issue warnings?


Moving on...


The value of a CompositeField will be represented by an instance of a
CompositeValue class. This will be a descendant of tuple and will
resemble namedtuple present in Python >= 2.5. It will support
iteration, numbered indexing and access to individual field values
using attributes corresponding to underlying field names. The order of
values will be the same as the order of fields specified in the model
definition.

Assigning a value to a CompositeField will be possible using any
iterable as long as its length equals the number of atomic fields (and
the values can be assigned to the corresponding fields, obviously).


Due to the nature of this field type, other lookup filters than
``exact`` and ``in`` would have unclear semantics and won't be
supported. The original plan was to also exclude support for ``in``
but as it turns out, ``in`` is used in several places under the
assumption that primary keys support it, for example DeleteQuery
or UpdateQuery. Therefore both filters will be implemented.


This should be everything as far as the models API is concerned. As
for the other parts of Django, the changes will be kept to a working
minimum.

Forms: Only support in ModelChoiceFields will be added for composite
primary keys; there won't be any special form field type for now.

Admin: Again, only support for composite primary keys will be added in
the quoting/unquoting function to make it possible to access such
models.

GFK: For now, GenericForeignKey won't be able to reference models with
composite primary keys.


I'm also thinking about implementing an abstract class, VirtualField.
This could be useful mainly as a base class for fields with no direct
database column. That means, it would mainly handle things like
add_to_class (adding itself to the list of virtual fields instead of
local ones), specifying arbitrary lookup filters when asked for one
etc. CompositeField could then be a descendant of this class.

However, I can't currently imagine any other use-case for this
abstract class than CompositeField. The question is, then, is there
any interest in having an abstract mechanism like this? Can anyone
imagine a use-case? (The question is, should I implement this
functionality directly inside CompositeField or factor it out into
something more general?)


I'll really appreciate each comment.

Michal Petrucha



[1] https://groups.google.com/forum/#!topic/django-developers/Eg1AHjAvNps
[2] 
https://groups.google.com/forum/#!topic/django-developers/Y0aAb792cTw/discussion
[3] http://people.ksp.sk/~johnny64/GSoC-full-proposal

Attachment: signature.asc
Description: Digital signature

Reply via email to