In Django, I would like the ability to mark certain model fields as required at the model (or at least database) level, to make sure that I am specifying them explicitly (i.e. not relying on defaults) when creating objects.
Currently, Django lets you designate a model field as required at the forms level (by specifying blank=False in the model). However, it doesn't seem like there is a similar option to get this behavior at the model or database level.
It does seem like there are various hacks to achieve something similar though. For example, you can set the default to something that violates a database constraint. For example, you can do things like:
models.CharField(_('characters'), max_length=4, default=None)
or
models.CharField(_('characters'), max_length=4, default="abcdef")
The former example works when saving to the database since None violates the default not-null constraint of null=False (raising an IntegrityError). The latter works because a DataError is raised when saving. But I don't know if this is guaranteed to work across all databases, etc.
Am I missing something, or is there a better way?
If django models called full_clean() automatically on save(), your check would run at the model level without a form. I've been playing with making this the default behavior in my django projects by creating an auto-clean model subclass which does full_clean() on save(), then deriving my models off that.
If you want to learn why it isn't already like this: Why doesn't django's model.save() call full_clean()?
Related
I have a User model with some fields. Some of them will require feedback, are they correctly filled (if not, specific message will be displayed on user profile).
The problem is, how to represent 'invalid' fields in database. My idea is to create another model (call it ExtUser) with OneToOneField to User. And ExtUser should have same fields' names as User, but their types will be all boolean, determining whether field is filled in correctly. For example, if User has a field called email:
email = models.CharField(max_length=100)
ExtUser would have following field:
email = models.BooleanField(default=False)
Here's a problem with this approach. How am I supposed to create fields in ExtUser? Of course I can create them manually, but that would be breaking of DRY principle, and I'm not going to do that. The question is, can I add fields to model dynamically, and have them in database (so I assume it would require to be called before migrate)?
I have django 1.8 and I don't want to use any external modules/libraries.
If someone has an another idea of how to represent that data in database, please add comment, not a reply - as this question is about creating fields dynamically.
You will need to do this manually.
Python does not disallow this behavior; you can take a look at this SO response on dynamically created classes, but Django will not be able to interpret the output. In particular, Django relies on the models to create the SQL tables for the application, and there is essentially no way for this to occur if you model is not statically defined.
In this case, I don't think you have to worry much about DRY; if you need a separate model with fields which happen to be related to, but different from, another model, I think it's probably ok.
Finally, I'm unsure what your goal is, but you could probably define some functions which can determine how "correct" the fields of the user are. This is how I would recommend solving this problem (if it applies).
So far I haven't come across a clear explanation on blank, null, and required - in Django's models and forms.
I know the default for each is the following:
blank = False
null = False
required = True
I also know that:
blank=True (used in models.py), means on the form level, accept empty forms - the associated field is not required in a form.
null=True (used in models.py), means on the database level that Python None values can be stored in the model and be saved (and then end up as SQL NULL values in the database).
required=False (used in forms.py), means the associated form field is not required.
Hopefully the above information will serve others well (please let me know if there are any flaws in the logic, and I will update it).
My question is the following:
When do I know when to use blank=True vs. required=False. If my goal is to make a form field not required, I could define this in the model using blank=True, or I could define this in the form using required=False. Does this mean you can define blank=True in a model, and in the associated ModelForm override this with required=True?
Also related, what about when you are using a regular form (forms.Form)? Since the form is not associated with a model (other than through view logic), what happens if again, they contradict each other?
The contradictions don't matter. I think this brings flexibility to the development of a Django application. For example, if you're using a third party library that defines some models, and they set blank=True, but for other purposes you need that field is mandatory, then in your forms you can fix it without touching the third party library code.
This just add some flexibility to the framework IMHO, that brings no harm. You can use it for your own purposes. The contradictions are not significant.
Hope this helps!
It depends on the requirement. Sometime we decide later to make field mandatory on the form although on model it is still not required. But the form will ensure that field must be fill.
You can have null=True on the model and then later you can make that field mandatory on form. But you can not make a field optional in form when it is mandatory on model that will result in database error later on.
I have have a many-to-many relation in a Django(1.4) model.
class UserProfile(models.Model):
foos = models.ManyToManyField(Foo)
I want to enforce that each User(Profile) has at least one Foo. Foos can have zero-or-more User(Profiles)s.
I would love this to be enforced at the model and admin levels, but just enforcing it in the admin would be sufficient.
If I understand correctly, 'many' in Django-speak is zero-or-more.
I want a ManyToOneOrMore relation. How can I do this?
Thanks,
Chris.
You can't enforce this on at the model level as #Greg details, but you can enforce it on a form by simply making the field required. This won't prevent anyone with shell-level access from manually creating a UserProfile without a foo, but it will force anyone using a browser-based form method of creation.
Unfortunately, I don't think this is possible at the model level because ManyToMany data is saved separately from other model fields. You should be able to enforce it at the admin level by specifying a custom form, and writing a clean() method on the form.
Following a related (as yet unanswered) question, I did some investigation and found that the current implementation of Django Haystack's RealTimeSearchIndex makes no attempt to also update on related field (Many to Many) changes. I thought this would be an easy fix - after all, I could just extend RealTimeSearchIndex like this:
class RealTimeM2MSearchIndex(RealTimeSearchIndex):
def _setup_save(self, model):
signals.m2m_changed.connect(self.update_object, sender=model)
signals.post_save.connect(self.update_object, sender=model)
But then I realized (or at least assumed, since it's not working) that this only works if the M2M field is defined on the model itself, and not if it's the "reverse" side of the M2M relationship. Trying to fix that, I then did something like the following:
signals.m2m_changed.connect(self.update_object, sender=model.related_field.through)
Where related_field is the name of the specific Model on other side of the ManyToMany definition. Strangely enough, upon running, Django then complains that the Model has no such field, related_field.
And sure enough, if I inspect the object, Django has not yet extended the model to have the related_field field. If I inspect the same object when displaying a view, however, it does have that related_field.
Summary
So the problem seems to be that Django's automatic behavior to add an attribute to the reverse side of an M2M relationship has yet to happen when Haystack runs its code. How can I overcome this obstacle, and allow Haystack's RealTimeSearchIndex to also update on related field changes?
I think the simplest solution is to just use the built in RealTimeSearchIndex, and add a signal listener in your models.py to reindex the model on m2m_changed, or whenever. See my answer to the other question - you could easily modify it to index on m2m_changed instead of post_save.
Just tried implementing this myself, and your problem is the value of the sender argument in this line:
signals.m2m_changed.connect(self.update_object, sender=model)
I read the documentation for the m2m_changed signal and the sender will be something like MyModel.my_field.through so you need to use that. This means you can't have a generic class as you are trying to do, but will need to define the _setup_save method in each case, with the m2m_changed signal connected for each ManyToMany field that model has.
For example, if your model had two ManyToManyFields called region and sector, you could do:
# we implement these to force the update when the ManyToMany fields change
def _setup_save(self, model):
signals.m2m_changed.connect(self.update_object,
sender=MyModel.sector.through)
signals.m2m_changed.connect(self.update_object,
sender=MyModel.region.through)
signals.post_save.connect(self.update_object, sender=model)
You should also really define the _teardown_save() method:
def _teardown_save(self, model):
signals.m2m_changed.disconnect(self.update_object,
sender=MyModel.sector.through)
signals.m2m_changed.disconnect(self.update_object,
sender=MyModel.region.through)
signals.post_save.disconnect(self.update_object, sender=model)
(This is based on code I have tested, it appears to work - certainly no errors about fields not existing).
Update: Just read your question more closely. Is it possible that your model has the ManyToManyField added dynamically? Is the call to register() after you have defined all your classes?
In the django admin documentation, it says the following:
By default, admin widgets for many-to-many relations will be displayed on whichever model contains the actual reference to the ManyToManyField.
Is there a way to get a similar widget to appear on the admin page for the other model, the one where the relationship isn't defined?
There's a couple different ways to get the effect that you're after.
Here's one way, which will get you a similar (but not identical) effect, and probably requires the least coding. (Examples will use classes A and B, assuming that the former has the many to many relationship explicitly defined)
The quickest way: you can use an InlineModelAdmin object:
class AInline(admin.TabularInline):
model = A
class BAdmin(admin.ModelAdmin):
inlines = (AInline,)
admin.site.register(B, BAdmin)
If you want the exact effect of getting a <select multiple>, the way you can do it is to use a custom Form class, and assign it to BAdmin.form.