Is there a model MultiField (any way to compose db models Fields in Django)? Or why would not that be a useful concept? - django

When building a Django application, we were exposed to (forms) MultiValueField and MultiWidget.
They seem like an interesting approach to compose their respective base classes, giving more modularity.
Yet, now it seems to us that the actual piece that would make those two shine bright would be a db.models.MultiField. Here is the reasoning:
It seems that, when using a ModelForm, Django is enforcing a strict 1-to-1 association between a models.Field and a forms.Field. Now, with forms.MultiValueField, despite this strict 1-to-1 association, you can have a single models.Field actually associated to the numerous forms.Field composing the forms.MultiValueField.
Yet, it is limited to the case where a single models.Field maps more naturally to several forms.Field. What seems very interesting would be the ability to associate any number of models.Fields to any number of forms.Field. The only piece that seems to be missing to get there is an hypothetical models.MultiField. It could communicate with the exterior through a compress() method (see. MultiValueField), and potentially a decompress() method in the other direction (see MultiWidget).
The questions would then be (assuming this requirement is not emerging from a misunderstanding of Django): Is there a way to compose modelds.Field in Django ? If not, why is such an empowering concept not implemented in this great framework ;) ?
EDIT: To give a motivating example, imagine we want to implement a partial date (a date that can be precise to a day, or just precise to a month and a year, or alternatively just a year), with a Model following the one presented in this answer, i.e.:
a DateField, representing the date
a CharField, to indicate whether the date is complete or month + year or just year.
This model is working just fine with the default ModelForm, but now we want to introduce some consistency check: if a date is only precise to the month (month + year or just year), its day part should be 1, and if it is only precise to the year, its month part should also be 1.
This is a cross-fields check (two different Fields from the ModelForm needs to be accessed to complete it), so it has to be implemented at the Form.clean() level. This check would need to be copy-pasted in each form containing a partial date, which goes against the DRY cherished by Django.
Now let's imagine Django is providing this hypothetical models.MultiField, which would be a composite models.Field. We could define a PartialDateclass deriving from MultiField and containing the two leaf fields defined above (DateField  and CharField). We could now say that the form field corresponding to this single model field (in a ModelForm) is a class derived from forms.MultiValueField. This class could implement the consistency check above, at the field level: it is not a cross-field check anymore.
This way, we got rid of the code duplication: any model could use a PartialDate field, automatically making any ModelForms mapping to it use the forms.MultiValueField implementing the consistency check, whose code was only written in one place.
(This is a simple example, it is easy to imagine it can get way more complex in production code, with consistency check you do not want to copy paste)

Possible: Yes. You could either subclass djangos model class, or monkey-patch that class into the existing model module.
Just (educated) guessing: I think it is not missing, but not needed.
In DB-Applications, combined fields will almost always come with special business rules. So you will have to implement a different display and validation for each one of them anyway.
Which you already can easily do in the models.Form.
Maybe you should look at customization of models.Form?

Related

How to create separate form widgets for ModelMultipleChoiceField in Django

I have a ManyToMany relationship between two Django Models: Team and Member. A single Member can be part of multiple teams. I am able to successfully bind a Form class to a CreateView, and, using the standard ModelMultipleChoiceField,can successfully save the Form using the save_m2m method.
However, the default widget for the field is not suitable for my user experience. Instead of using a picklist, I would like to create a separate select box for each number of selectable Members per team. For example, if the Team can have 7 Members, I would like to show 7 select boxes, rather than one pick list that a user selects 7 different objects from.
I understand I may not get a complete answer, but would appreciate any pointers on if I should be looking into overriding the Field with a custom MultiWidget, or if using an inline formset might be a more appropriate route. Or, something else...
At the end of the day, I ultimately decided to stick with a single ModelForm for the Create Team form. Within this ModelForm, I dynamically create the number of fields I will need for the number of Member per team, and have created a function within the form that yields those fields.
I perform validation on those fields within the generic clean method of the ModelForm. I use the yield method within the Template to control displaying the individual fields.
I am sure it is not the most Django-y approach, as many people seem to use inlineformsets in theory, but this worked and the code does not seem overly hacky or unmaintainable.

Validation of model with incomplete data in Django

I have a following use case in a project I'm working on.
Workflow looks like this:
the system accepts proposed candidates (at this stage most fields are not required, email address can be invalid, etc.)
their information can be corrected and updated
some of the candidates get registered (now fields such as name and last name are required, email has to be valid, etc.)
I came up with two ideas.
One is to have two models for candidates. Then I could leverage automatic validation from Models and ModelForms but it would require copying instances from one model to the other while registering candidates and would bring problems in different places (for example with ForeignKeys).
Second idea is to have one model that accepts incomplete data and two ModelForms, one with redefined fields.
Both ideas require duplication of quite similar code.
Does anyone know about DRY and Django-style way of approaching this problem?
You can define a model without any database restrictions and then implement two different (Model)Forms.
Form A is used for the input of new objects. Thus, your Form A should not contain specific validation logic.
Form B on the other hand can hold all your validation logic and can be used to maintain data integrity.
Please note, that this approach will not guarantee database integrity. Your validation logic should be exposed to heavy unittesting.
It sounds like you need a model for the proposed candidate info and another model for the final registered candidate, if you want to save the intermediate stage on the database and allow a user to edit it on another visit. Separate models for separate concerns. To reiterate your first option :)
Editing this as I realise it's all just rehashing your question. In this situation I'd stick with option 1, creating a new instance with the final candidate info when necessary. I'd probably link the final candidate to the proposed candidate instance with a foreign key and have null=True in the field definition, for the initial case. This would allow a cleanup task to run periodically and remove the proposed candidate instances where the data is duplicated for registered candidates. Or you could bite the bullet and remove the proposed instance upon a successful save of the final instance. Proceed with caution; you probably don't need to be thinking about removing stale data unless it becomes a problem, so I digress.
There's some slight duplication of code here, but not a great deal, and sometimes it's unavoidable if you want to reduce the potential for hairy application logic surprises.

What are the pros and cons of using GenericForeignKey vs multitable inheritance vs OneToOneField?

Context
I am in the process of modeling my data using Django models.
The main model is an Article. It holds the actual content.
Then each Article must be attached to a group of articles. Those group may be a Blog, a Category a Portfolio or a Story. Every Article must be attached to one, and exactly one of those. That is, either a blog, a category or a story. Those models have very different fields and features.
I thought of three ways to reach that goal (and a bonus one that really looks wrong).
Option #1: A generic foreign key
As in django.contrib.contenttypes.fields.GenericForeignKey. It would look like this:
class Category(Model):
# some fields
class Blog(Model):
# some fields
class Article(Model):
group_type = ForeignKey(ContentType)
group_id = PositiveIntegerField()
group = GenericForeignKey('group_type', 'group_id')
# some fields
On the database side, that means no relation actually exists between the models, they are enforced by Django.
Option #2: Multitable inheritance
Make article groups all inherit from an ArticleGroup model. This would look like this:
class ArticleGroup(Model):
group_type = ForeignKey(ContentType)
class Category(ArticleGroup):
# some fields
class Blog(ArticleGroup):
# some fields
class Article(Model):
group = ForeignKey(ArticleGroup)
# some fields
On the database side, this creates an additional table for ArticleGroup, then Category and Blog have an implicit foreign key to that table as their primary key.
Sidenote: I know there is a package that automates the bookkeeping of such constructions.
Option #3: manual OneToOneFields
On the database side, it is equivalent to option #2. But in the code, all relations are made explicit:
class ArticleGroup(Model):
group_type = ForeignKey(ContentType)
class Category(Model):
id = OneToOneField(ArticleGroup, primary_key=True)
# some fields
class Blog(Model):
id = OneToOneField(ArticleGroup, primary_key=True)
# some fields
class Article(Model):
group = ForeignKey(ArticleGroup)
# some fields
I don't really see what the point of that would be, apart from making explicit what Django's inheritance magic implicitly does.
Bonus: multicolumn
It seems pretty dirty so I just add it as a bonus, but it would also be possible to define a nullable ForeignKey to each of Category, Blog, ... directly on the Article model.
So...
...I cannot really decide between those. What are the pros and cons of each approach? Are there some best practices? Did I miss a better approach?
If that matters, I'm using Django 1.8.
It seems noone had advice to share on that one.
I eventually chose the multicolumn option, despite having said it looked ugly. It all came down to 3 things:
Database-based enforceability.
The way Django ORM works with the different constructs.
My own needs (namely, collection queries on the group to get the item list, and individual queries on the items to get the group).
Option #1
Cannot be enforced at the database level.
Could be efficient on queries because the way it is constructed does not fall into usual generic foreign key pitfalls. Those happen when the items are generic, not the collections.
However, due to how the ORM handles GFK, it is impossible to use a custom manager, which I need because my articles are translated using django-hvad.
Option #2
Can be enforced at the database level.
Could be somewhat efficient, but runs into ORM limitations, which is clearly not built around this use. Unless I use extra() or custom queries alot, but at some point there is no reason to use an ORM anymore.
Option #3
Would actually be a bit better than #2, as making things explicit allows easier query optimisation while using the ORM.
Multicolumn
Turns out not being so bad. It can be enforced at the database level (FK constraints plus a manual CHECK to ensure only one of the columns is non-null).
Easy and efficient. A single intuitive query does the job: select_related('category', 'blog', ...).
Though it does have the issue of being harder to extend (any new type will require altering the Article's table as well) and limiting the possible number of types, I'm unlikely to run into those.
Hope it helps anyone with the same dilemma, and still interested in hearing other opinions.

database design for multiple similar content types

I've worked on multiple sites recently with similar content types but haven't gotten the design I'm looking to achieve.
I have multiple types of content article, interview, video, gallery, blog, etc. All of these models have very similar properties (title, slug, body, pub_date, etc). And since I'm using django and the admin, almost all the admin setting are identical as well. Most will only have one or two additional fields (ie. filename for video, author for blog).
Currents options are
Using single model "Post/Article" and then just have a type_of_content field. This gives me a single model which makes searches easier and faster and its easy to maintain one model. Managers could be used to pull certain types of content.
Have models 'Video, Interview, Audio' subclass a model called "Post/Article". Gains flexibility of working with different models without all the redundacy. Lots of joins though and all the admin code is still duplicated.
Be very redundant and create a separate model for each type of content even though they share the majority of fields. More stuff to maintain, not DRY at all but highest level of flexibility.
Any insight from someone with more experience would be great.
Thank you.
I don't have that much experience with Django, but it sounds like what you want to do is subclass off of an Abstract Base Class. This avoids creating a table for the abstract parent class, so you get the advantage of your option #2 without the need for joins.

How can I use django forms/models to represent choices between fields?

How can I use boolean choices in a model field to enable/disable other fields. If a boolean value is true/false I want it to enable/disable other model fields. Is there a way to natively express these relationships using django models/forms/widgets? I keep writing custom templates to model these relationships, but can't figure out a good way to represent them in django without a special template.
For example:
class PointInTime(models.Model):
is_absolute_time = models.BooleanField()
absolute_time = models.DateTimeField()
is_relative_time = models.BooleanField()
days_before = models.IntegerField()
So if the is_absolute_time is True, I want the absolute_time entry to be editable in the GUI and the days_before entry to be grayed out and not-editable. If the 'is_relative_time' flag is True, I want the absolute_time entry to be grayed out, and the days_before value to be editable. So is_absolute_time and is_relative_time would be radio buttons in the same Group in the GUI and their two corresponding fields would only be editable when their radio button is selected. This is easy to do in a customized template, but is there a way to use a model/form in django to natively show this relationship?
It would be helpful to clarify what you mean by "natively show this relationship," and think clearly about separation of concerns.
If all you want is to "gray out" or disable a certain field based on the value of another field, this is purely a presentation/UI issue, so the template (and/or Javascript) is the appropriate place to handle it.
If you want to validate that the submitted data is internally consistent (i.e. absolute_time is filled in if is_absolute_time is True, etc), that's a form-validation issue. The place for that logic is in the clean() method of your Form or ModelForm object.
If you want to ensure that no PointInTime model can ever be saved to the database without being internally consistent, that's a data-layer concern. The place for that is in a custom save() method on your model object (Django 1.2 will include a more extensive model validation system).
All of those options involve writing imperative code to do what you need with these specific fields. It may be that you're looking for a way to represent the situation declaratively in your model so that the code in all three of the above cases can be written generically instead of specifically. There's no built-in Django way to do this, but you could certainly do something like:
class PointInTime(models.Model):
field_dependencies = {'is_absolute_time': 'absolute_time',
'is_relative_time': 'days_before'}
... fields here ...
Then your model save() code (or your Form clean() code, or your template), could use this dictionary to determine which fields should be enabled/disabled depending on the value of which other one. This generalization is hardly worth the effort, though, unless you anticipate needing to do this same thing in a number of different models.
Lastly, a few schema design alternatives you may want to consider to get your data layer better normalized:
If there are only two valid states (absolute and relative), use a single boolean field instead of two. Then you avoid possible inconsistencies (what does it mean if both booleans are False? Or True?)
Or simplify further by eliminating the booleans entirely and just using Null values in one or the other of absolute_time/days_before.
If there might be more than two valid states, use a single IntegerField or CharField with choices instead of using two boolean fields. Same reason as above, but can accomodate more than two options.
Since a RelativeTime and an AbsoluteTime don't appear to share any data fields with each other, consider splitting them out into separate models entirely. If you have other models that need a ForeignKey to either one or the other, you could model that with inheritance (both RelativeTime and AbsoluteTime inherit from PointInTime, other models have ForeignKeys to PointInTime).
I'm not entirely sure what you're doing with these objects but whichever the user chooses, you're pointing to a single moment in time. "5 days ago" is "Thursday" and vice-versa.
So unless the dates roll with the site (eg the record with "5 days ago" will still mean Thursday, tomorrow, etc), surely this is only an interface problem? If that's the case, I'd stick with a single value for the date in your Model and let the form and view do all the work.
That solves the auto-generated Admin side of things as you'll just have one field to contend with but it won't natively give you the choice between the two unless you write your own form widget and override the ModelAdmin class for your Model.
If this isn't the case, please ignore this answer.