Considering the past two proposals I've made here, I doubt I'll get more 
than an echo chamber effect on this one.

For the past week or so I've spent a bit of time on a feature I've always 
wanted to see land in django -- composite fields. The tickets have been 
open in the bug tracker for quite some time (and there's a few related 
ones, such as multi-column primary keys that can all be killed with the one 
stone).

The work is available on this branch of my fork of django 
<https://github.com/ovangle/django/tree/composite_fields> for the moment -- 
I haven't opened up a PR yet because there's still some features that are 
still to be implemented that will be explained below, but I want to give 
everybody a chance to tell me where I can stick it before I spend *too* 
much time on it.

So, without further ado, the proposal.


Composite Fields - Implemented

A composite field is an encapsulation of the functionality of a subset of 
fields on a model. Composite fields can be defined in one of two ways:

1. Statically declared composite fields

A statically declared composite field is defined in the same way a django 
model is defined. There are two customisable transformation functions, 
CompositeField.value_to_dict(self, value) and 
CompositeField.value_from_dict(self, value) which can be used to associate 
the field with a python object.

All the serialization functions are implemented via the implementations of 
the subfields.

For example,  

class MoneyField(models.CompositeField):
   currency_code = models.CharacterField(max_length=3)
   amount = models.DecimalField(max_digits=16, decimal_digits=4)

   ## Overriding __init__ can be used to pass field parameters to the 
subfields

   def value_from_dict(self, value):
       if value is None:
          return None
       return Money(**value)

   def value_to_dict(self, value):
      if value is None:
         return None
      return {attr: getattr(value, attr) for attr in ('currency_code', 
'amount')}

2. Inline composite fields.

An inline composite field is declared at the field definition site on the 
body of a model, by providing the subfields as the 'fields' argument of the 
CompositeField constructor. There are no transformation parameters 
available to override when declaring a composite field in this fashion -- 
the value of the field is always available as a python `dict` as an 
attribute on the MyModel

class MyModel(models.Model):
    id = models.CompositeField(fields = [
       ('a', models.IntegerField()),
       ('b', models.CharField(max_length=30)
    ], primary_key=True)

This method for defining composite fields has a few drawbacks, but can be 
useful if the only reason to add the composite field to the model was to 
implement a unique_together or index_together constraint *

* Although it's still possible to do that directly on class Meta.

3. Null

Setting the value of a multi-column field to NULL is different than setting 
any of the individual subfields to NULL. But there are cases (e.g. Money) 
where we would like to be able to set `null=True` on a composite field, but 
still retain 'NOT NULL' constraints on each of the subfield columns.

To solve this problem, every table which implements a CompositeField will 
also add an implicit (semi-hidden) `isnull` subfield on the attribute, 
which keeps track of whether it is the value of the composite field that is 
null, or any of the particular subfields.

3. Querying.

The syntax for querying over the subfields of a composite field will be 
familiar to anyone who has queried over a relationship attribute in django.

model.objects.filter(price__currency_code='USD', 
price__amount__lt=Decimal('50.00')).all()

In addition, composite fields can be queried via EXACT and IN lookups. It 
is possible to implement custom lookups for specific statically defined 
fields, but not recommended and not part of the official API.

4. Restrictions

The following restrictions are currently imposed on the use of composite 
fields. None of these are restrictions that can't be worked around in 
future extensions, but they're restrictions which considerably simplify 
both the implementation and API.

- No related fields as a subfield of a composite field
- No nested composite fields
- No inheritance of composite fields (apart from inheriting from 
CompositeField itself).

5. Changes to the Field API

As discussed in the other thread I posted. I've changed the implementation 
so that _get_cache_name can still be dependent on the name, but I think 
using attname is more useful anyway.

Composite Fields -- unimplemented

These features are still not implemented

- multi column primary keys. unique_together and index_together are 
implemented and adding a primary key constraint should be a similar 
operation.
- some small issues with multi-table inheritance.
- more test coverage
- proper documentation
- anything that comes out of this thread.


  

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
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/ed326cce-6784-429b-869b-f6f66d3c77fd%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to