How can I get all the fields that is required for a model to be created?
(same as django migrations check before running migrations?)
You can use the get_fields() method on the model Meta class:
fields = MyModel._meta.get_fields()
required_fields = []
# Required means `blank` is False
for f in fields:
# Note - if the field doesn't have a `blank` attribute it is probably
# a ManyToOne relation (reverse foreign key), which you probably want to ignore.
if getattr(f, 'blank', False):
required_fields.append(f)
The answer above can be implemented slightly more concisely using list comprehension and getattr() with a default of False:
required_fields = [f for f in MyModel._meta.get_fields() if not getattr(f, 'blank', False) is True]
Here is a more thorough way of getting a list of all required fields.
First, it gets all the fields from the model where blank=False.
Second, it checks to see if you set required=False for any base fields in the form class.
Third, it updates the list to give you all required fields.
One thing this function does not do is check whether you manually set required=True to any fields in the form class' init method.
def get_required_fields(form_class):
"""Get the required fields of a form. Returns a list of field names."""
def get_blank_not_allowed_fields(model):
"""Get the list of field names where 'blank=False' for the given model."""
blank_not_allowed_fields = []
for field in model._meta.get_fields():
if hasattr(field, 'blank') and field.blank is False:
blank_not_allowed_fields.append(field.name)
blank_not_allowed_fields.remove('id')
return blank_not_allowed_fields
blank_not_allowed_fields = get_blank_not_allowed_fields(form_class.Meta.model)
# Even though, for certain fields, blanks are designated to be allowed (aka blank=True)
# in the model class, they can have 'required' != True in the form class.
# So, remove all fields where required is not True.
for field_name, value in form_class.base_fields.items():
if value.required is not True:
blank_not_allowed_fields.remove(field_name)
# At this point, if we are being precise, this variable should be named required_fields
# but, there is no point in changing the name of an already declared list.
return blank_not_allowed_fields
Having the following models:
#models.py
Column(models.Model):
name = models.CharField()
Input(models.Model):
column = ForeignKey(Column)
text = models.CharField()
And the following form:
class InputForm(Modelform):
class Meta():
model=Input
The following works:
c = Column.objects.get(...)
i=InputForm({'column':c.id})
i.is_valid() #true
In my application I am generating many forms, to avoid clashes I prefix it:
i=InputForm({'column':c.id}, prefix=prfx()) #prfx() is dynamically generated
i.errors # ({'column':['This field is required']})
i.data['column'] is still the right value
I also tried:
i.column = c
i.errors # ({'column':['This field is required']})
How do I populate the column field?
I cannot save the form as long as it does not validate
EDIT What I am trying to achieve:
I am building dynamic forms:
form_list = [InputForm(column, prefix=column.id) for column in col_list]
In the template I iterate over this form list (and I want to set the column field to be invisible.
{% for form in form_list %}
{{form.as_ul}}
{%endfor%}
This form then shall be processed by an AjaxView. The relation between text and column is achieved by the invisible field.
The first parameter to the form is the input data, which is usually request.POST. If you render a Form with a prefix, you will see that all form html elements will have a prefixed name, for example <input name='yourprefix_text' /> etc. If you POST such a form to your Django app, the POSTed data will have the prefix, too.
So, if you are using a prefix, the input data needs to be prefixed, too:
f = InputForm({'yourprefix_column': c.id}, prefix='yourprefix')
Usually, it is a better idea to use the initial parameter to the form for default values, because otherwise the form is always bound, and this has some consequences, for example default/initial values for other fields are will not work.
f = InputForm(prefix='yourprefix', initial={'column': c})
# or ...
form_list = [InputForm(prefix=column.id, initial={'column': column})
for column in col_list]
If you want to always set the column to a programatically determined value, and not allow the user to change it, it is better to not include the field in your form, and set the field manually after saving the object:
f = InputForm(request.POST)
if f.is_valid():
instance = f.save(commit=False)
instance.column = c
instance.save()
To make your field hidden, you can change the widget, as described in the documentation:
class InputForm(ModelForm):
class Meta:
widgets = {
'column': forms.HiddenInput,
}
# ...
I am learning Django,looked into django validation but the below type i want.searched in google no result.
In my app,their are two character fields,i want it to be validate so that the conditons are,
1.Either any one of the field is entered.
2.It should validate the entered data are integer.
that means,both fields are not mandatory,but any one is mandatory and that mandatory field should accept number only.
How to do it in django.
class MyForm(forms.Form):
field_one = forms.IntegerField(required=False)
field_two = forms.IntegerField(required=False)
def clean(self):
cleaned_data = self.cleaned_data
field_one = cleaned_data.get('field_one')
field_two = cleaned_data.get('field_two')
if not any([field_one, field_two]):
raise forms.ValidationError(u'Please enter a value')
return cleaned_data
Using an IntegerField will validate that only numeric characters are
present, covering your blank space use case.
Specifying required=False on both fields allows either field to be left blank.
Implementing clean() on the form gets you access to both fields.
.get() will return None if the key isn't found, so the use of
any([field_one, field_two]) will return true if at least one of the
values in the list isn't None. If neither value is found, the
ValidationError will be raised.
Hope that helps you out.
I'm trying to write a method like the below where a list of fields (a subset of all the fields) is passed in as a parameter and has their column values set to null. I would be happy of I could get a method with just the fields as a parameter like below, but having the model as a parameter would be even better.
from my_project.my_app.models import MyModel
def nullify_columns (self, null_fields):
field_names = MyModel._meta.get_all_field_names()
for field in field_names:
if field in null_fields:
# The below line does not work because I'm not sure how to
# dynamically assign the field name.
MyModel.objects.all().update( (MyModel.get_field(field).column) = None)
Right now I have something like
if 'column1' in list_of_fields:
MyModel.objects.all().update(column1 = None)
if 'column2' in list_of_fields:
MyModel.objects.all().update(column2 = None)
etc. which is horrible, but works.
It's in the tutorial:
MyModel.objects.all().update(**dict.fromkeys(null_fields))
I have model Foo which has field bar. The bar field should be unique, but allow nulls in it, meaning I want to allow more than one record if bar field is null, but if it is not null the values must be unique.
Here is my model:
class Foo(models.Model):
name = models.CharField(max_length=40)
bar = models.CharField(max_length=40, unique=True, blank=True, null=True, default=None)
And here is the corresponding SQL for the table:
CREATE TABLE appl_foo
(
id serial NOT NULL,
"name" character varying(40) NOT NULL,
bar character varying(40),
CONSTRAINT appl_foo_pkey PRIMARY KEY (id),
CONSTRAINT appl_foo_bar_key UNIQUE (bar)
)
When using admin interface to create more than 1 foo objects where bar is null it gives me an error: "Foo with this Bar already exists."
However when I insert into database (PostgreSQL):
insert into appl_foo ("name", bar) values ('test1', null)
insert into appl_foo ("name", bar) values ('test2', null)
This works, just fine, it allows me to insert more than 1 record with bar being null, so the database allows me to do what I want, it's just something wrong with the Django model. Any ideas?
EDIT
The portability of the solution as far as DB is not an issue, we are happy with Postgres.
I've tried setting unique to a callable, which was my function returning True/False for specific values of bar, it didn't give any errors, however seamed like it had no effect at all.
So far, I've removed the unique specifier from the bar property and handling the bar uniqueness in the application, however still looking for a more elegant solution. Any recommendations?
Django has not considered NULL to be equal to NULL for the purpose of uniqueness checks since ticket #9039 was fixed, see:
http://code.djangoproject.com/ticket/9039
The issue here is that the normalized "blank" value for a form CharField is an empty string, not None. So if you leave the field blank, you get an empty string, not NULL, stored in the DB. Empty strings are equal to empty strings for uniqueness checks, under both Django and database rules.
You can force the admin interface to store NULL for an empty string by providing your own customized model form for Foo with a clean_bar method that turns the empty string into None:
class FooForm(forms.ModelForm):
class Meta:
model = Foo
def clean_bar(self):
return self.cleaned_data['bar'] or None
class FooAdmin(admin.ModelAdmin):
form = FooForm
** edit 11/30/2015: In python 3, the module-global __metaclass__ variable is no longer supported.
Additionaly, as of Django 1.10 the SubfieldBase class was deprecated:
from the docs:
django.db.models.fields.subclassing.SubfieldBase has been deprecated and will be removed in Django 1.10.
Historically, it was used to handle fields where type conversion was needed when loading from the database,
but it was not used in .values() calls or in aggregates. It has been replaced with from_db_value().
Note that the new approach does not call the to_python() method on assignment as was the case with SubfieldBase.
Therefore, as suggested by the from_db_value() documentation and this example, this solution must be changed to:
class CharNullField(models.CharField):
"""
Subclass of the CharField that allows empty strings to be stored as NULL.
"""
description = "CharField that stores NULL but returns ''."
def from_db_value(self, value, expression, connection, contex):
"""
Gets value right out of the db and changes it if its ``None``.
"""
if value is None:
return ''
else:
return value
def to_python(self, value):
"""
Gets value right out of the db or an instance, and changes it if its ``None``.
"""
if isinstance(value, models.CharField):
# If an instance, just return the instance.
return value
if value is None:
# If db has NULL, convert it to ''.
return ''
# Otherwise, just return the value.
return value
def get_prep_value(self, value):
"""
Catches value right before sending to db.
"""
if value == '':
# If Django tries to save an empty string, send the db None (NULL).
return None
else:
# Otherwise, just pass the value.
return value
I think a better way than overriding the cleaned_data in the admin would be to subclass the charfield - this way no matter what form accesses the field, it will "just work." You can catch the '' just before it is sent to the database, and catch the NULL just after it comes out of the database, and the rest of Django won't know/care. A quick and dirty example:
from django.db import models
class CharNullField(models.CharField): # subclass the CharField
description = "CharField that stores NULL but returns ''"
__metaclass__ = models.SubfieldBase # this ensures to_python will be called
def to_python(self, value):
# this is the value right out of the db, or an instance
# if an instance, just return the instance
if isinstance(value, models.CharField):
return value
if value is None: # if the db has a NULL (None in Python)
return '' # convert it into an empty string
else:
return value # otherwise, just return the value
def get_prep_value(self, value): # catches value right before sending to db
if value == '':
# if Django tries to save an empty string, send the db None (NULL)
return None
else:
# otherwise, just pass the value
return value
For my project, I dumped this into an extras.py file that lives in the root of my site, then I can just from mysite.extras import CharNullField in my app's models.py file. The field acts just like a CharField - just remember to set blank=True, null=True when declaring the field, or otherwise Django will throw a validation error (field required) or create a db column that doesn't accept NULL.
You can add UniqueConstraint with condition of nullable_field=null and not to include this field in fields list.
If you need also constraint with nullable_field wich value is not null, you can add additional one.
Note: UniqueConstraint was added since django 2.2
class Foo(models.Model):
name = models.CharField(max_length=40)
bar = models.CharField(max_length=40, unique=True, blank=True, null=True, default=None)
class Meta:
constraints = [
# For bar == null only
models.UniqueConstraint(fields=['name'], name='unique__name__when__bar__null',
condition=Q(bar__isnull=True)),
# For bar != null only
models.UniqueConstraint(fields=['name', 'bar'], name='unique__name__when__bar__not_null')
]
Because I am new to stackoverflow I am not yet allowed to reply to answers, but I would like to point out that from a philosophical point of view, I can't agree with the most popular answer tot this question. (by Karen Tracey)
The OP requires his bar field to be unique if it has a value, and null otherwise. Then it must be that the model itself makes sure this is the case. It cannot be left to external code to check this, because that would mean it can be bypassed. (Or you can forget to check it if you write a new view in the future)
Therefore, to keep your code truly OOP, you must use an internal method of your Foo model. Modifying the save() method or the field are good options, but using a form to do this most certainly isn't.
Personally I prefer using the CharNullField suggested, for portability to models I might define in the future.
The quick fix is to do :
def save(self, *args, **kwargs):
if not self.bar:
self.bar = None
super(Foo, self).save(*args, **kwargs)
This is fixed now that https://code.djangoproject.com/ticket/4136 is resolved. In Django 1.11+ you can use models.CharField(unique=True, null=True, blank=True) without having to manually convert blank values to None.
Another possible solution
class Foo(models.Model):
value = models.CharField(max_length=255, unique=True)
class Bar(models.Model):
foo = models.OneToOneField(Foo, null=True)
I recently had the same requirement. Instead of subclassing different fields, I chose to override the save() metod on my model (named 'MyModel' below) as follows:
def save(self):
"""overriding save method so that we can save Null to database, instead of empty string (project requirement)"""
# get a list of all model fields (i.e. self._meta.fields)...
emptystringfields = [ field for field in self._meta.fields \
# ...that are of type CharField or Textfield...
if ((type(field) == django.db.models.fields.CharField) or (type(field) == django.db.models.fields.TextField)) \
# ...and that contain the empty string
and (getattr(self, field.name) == "") ]
# set each of these fields to None (which tells Django to save Null)
for field in emptystringfields:
setattr(self, field.name, None)
# call the super.save() method
super(MyModel, self).save()
If you have a model MyModel and want my_field to be Null or unique, you can override model's save method:
class MyModel(models.Model):
my_field = models.TextField(unique=True, default=None, null=True, blank=True)
def save(self, **kwargs):
self.my_field = self.my_field or None
super().save(**kwargs)
This way, the field cannot be blank will only be non-blank or null. nulls do not contradict uniqueness
For better or worse, Django considers NULL to be equivalent to NULL for purposes of uniqueness checks. There's really no way around it short of writing your own implementation of the uniqueness check which considers NULL to be unique no matter how many times it occurs in a table.
(and keep in mind that some DB solutions take the same view of NULL, so code relying on one DB's ideas about NULL may not be portable to others)