How could I add rules to a Django Model making certain fields that aren't required by default, required if another field is set. Or even the other way around
Let's say I have this Model:
class Item(models.Model):
name = models.CharField(max_length = 75)
cant_be_sold = models.BooleanField()
flat_price = models.IntegerField(blank = True, null = True, default = None, validators = [MinValueValidator(0)])
defense = models.IntegerField(blank = True, null = True, default = None, validators = [MinValueValidator(0)])
required_classes = models.ManyToManyField('otherappname.Class', related_name = 'Requires_Classes', blank = True, null = True, default = None)
Let's say there can be 2 situations here;
I mark cant_be_sold as True; now flat_price can only be None (NULL)
I fill in defense; now it IS required to select one or more required_classes
I wonder what the nice way to do this in Django is. Would help me a bunch to prevent wrongfully entries, as my Item model has over 70 property fields, due to the great extend of Item Variances in my system.
Write a clean method for your model. In it, you can change field values, and raise validation errors. The following example should get you started.
def clean(self):
if self.cant_be_sold and self.flat_price is not None:
raise ValidationError("flat_price must be None when cant_be_sold is True")
Related
I am developing an app with Django where I have to model systems.
A system is defined by a number of standard fields (name, owner, etc...) and then a tree of assets (implemented using mptt). A given asset can be part of several systems. A node in the tree of assets can point only to a given asset.
So, I have a model to represent the assets (simplified):
class Asset(models.Model):
name = models.CharField(verbose_name = 'name', max_length = 64, unique = True, blank = False, null = False)
I have a class to represent the system trees (simplified):
class System_Tree(MPTTModel):
parent = TreeForeignKey('self',verbose_name = 'parent', on_delete = models.CASCADE, blank = True, null = True, related_name = 'children')
asset = models.ForeignKey(Asset, verbose_name = 'asset', on_delete = models.CASCADE, blank = False, null = False)
I have a class to represent the system (simplified):
class System(models.Model):
name = models.CharField(verbose_name = 'name', max_length = 64, unique = True, blank = False, null = False)
I would like to show in a template the system with its fields and its specific system tree, but I don't know how to relate a specific instance of a system tree (maybe identified by a root node (parent = null)) with the System model.
Should I define a new field type representing system trees?
Should I store only a reference to the parent node id as a big integer field?
Is there any way then to automate the admin interface to show the right forms for a system (the standard fields and then a tree for the system tree)?
Thanks in advance!!
I have a model on which the unique_together parameter is not working. The reason is that most of the times the "client_repr" variable is set on the save() method.
If someone creates a task with the same ('client_repr', 'task_type'... ) combination the model won't detect it because the "client_repr" value is null until the end of the save() method.
How can i call for a unique constraint verification inside the save() method?
class Task(models.Model):
client = models.ForeignKey(Client, related_name = 'tasks', on_delete = models.CASCADE)
b_client = models.ForeignKey(BClient, related_name = 'tasks', on_delete = models.CASCADE, null = True, blank = True)
client_repr = models.CharField(max_length = 100, null = True, blank = True)
task_type = models.CharField(max_length = 100)
task_description = models.CharField(max_length = 100)
department = models.ForeignKey(Department, related_name = 'tasks', on_delete = models.CASCADE)
extra_fields = models.ManyToManyField(ExtraField, blank = True)
spot = models.BooleanField(default = False)
class Meta:
unique_together = (('client_repr', 'task_type', 'task_description', 'department'), )
def __str__(self):
return ' | '.join([f'{self.client_repr}', f'{self.task_description}', f'{self.task_type}'])
def save(self, *args, **kwargs):
if not self.b_client:
self.client_repr = str(self.client)
else:
self.client_repr = str(self.b_client)
super().save(*args, **kwargs)
I know i could just make a search (ex: if Task.objects.get(...): ) but is it the most django-pythonic way?
Option 1: Using clean()
It's not directly what you asked, but it's generally more django-friendly and does not require weird things like overriding save()
class Task(Model):
def clean():
# your code starts here
if not self.b_client:
self.client_repr = str(self.client)
else:
self.client_repr = str(self.b_client)
# your code ends here
Since this custom clean() is called before django calls validate_unique(), it should fulfill your requirements.
See the details in the official documentation.
Option 2: Continue doing everything in save()
To check unique constraints, you can do the following:
from django.core.exceptions import ValidationError
def save(self, *args, **kwargs):
... # your code that automatically sets some fields
try:
self.validate_unique()
# self.full_clean() # <- alternatively, can use this to validate **everything**, see my comments below
except ValidationError:
# failed
# that's up to you what to do in this case
# you cannot just re-raise the ValidationError because Django doesn't expect ValidationError happening inside of save()
super().save(*args, **kwargs)
Note:
doing only self.validate_unique() does not guarantee, that the updated earlier in save() values are good and don't violate something else
self.full_clean() is safer, but will be slightly slower (how slow - depends on the validators you have)
Documentation
Django documentation says:
There are three steps involved in validating a model:
Validate the model fields - Model.clean_fields()
Validate the model as a whole - Model.clean()
Validate the field uniqueness - Model.validate_unique()
All three steps are performed when you call a model’s full_clean() method.
I have a MySQL database object that stores concerts that a User plans to attend on a certain date. Since each concert entry needs to be associated with a User, I added a foreign key field for User as an attribute of the Concert model. Here is the complete model:
class Concert(models.Model):
user = models.ForeignKey(User, on_delete = models.CASCADE)
concert_name = models.CharField(max_length = 120, blank = True, null = True)
venue = models.CharField(max_length = 120, blank = True, null = True)
city = models.CharField(max_length = 120, blank = True, null = True)
state = models.CharField(max_length = 120, blank = True, null = True)
country = models.CharField(max_length = 120, blank = True, null = True)
timestamp = models.DateTimeField(auto_now_add = True, auto_now = False)
updated = models.DateTimeField(auto_now_add = False, auto_now = True)
When I try to make migrations to the DB, I get a message saying: You are trying to add a non-nullable field 'user' to concert with out a default; s essentially saying that the already populated rows need to have a value. I found this SO post which seemed to be addressing a similar issue, but the default value suggested in the solution is for a charfield and I don't think that would really apply in this situation. How should I go about setting the default value of the User object?
You can use null=True:
user = models.ForeignKey(User, null=True)
Although if you look at your db actual model, you cannot see an actual Foreign Key, when you put models.ForeignKey(User) in your model you can see in your db a user_id with references, but the relation needed exists so you can make it's null=True as a default.
Table in PostgreSQL database:
CREATE TABLE pmss_recordmodel
(
id serial NOT NULL,
"Name" character varying(100),
CONSTRAINT pmss_recordmodel_pkey PRIMARY KEY (id)
)
WITH (OIDS=FALSE);
ALTER TABLE pmss_recordmodel OWNER TO postgres;
Model:
class RecordModel(models.Model):
def __unicode__(self):
return self.Name
Name = models.CharField(max_length = 100, unique = False, null = True, blank = True)
When I POST data with blank Name field, form.is_valid() returns False. Why? Am I missing something?
EDIT:
class RecordForm(forms.ModelForm):
class Meta:
model = RecordModel
Name = forms.CharField(label = "Имя ", widget = forms.TextInput(attrs = {'size': 15}))
Django forms documentation:
By default, each Field class assumes the value is required
Set the "required" argument to False as such:
Name = forms.CharField(required = False, label = "Имя ", widget = forms.TextInput(attrs = {'size': 15}))
You could be having problems with how the field is defined in your database.
This seems like it could be the common situation of doing syncdb with the field initially not having blank=True null=True and then adding those terms later (after initial syncdb) to make the field not required. These settings will not be applied by simply running syncdb again. It requires a database migration or a database flush (will clear existing data, which isn't necessarily bad in development).
I have models...
class Item(models.Model):
name = models.CharField('Item Name', max_length = 30)
item_code = models.CharField(max_length = 10)
color = models.CharField(max_length = 150, null = True, blank = True)
size = models.CharField(max_length = 30, null = True, blank = True)
fabric_code = models.CharField(max_length = 30, null = True, blank = True)
I have values in Item. In the Item model name field has the similar values (but the other values of record are changed). I want to select the name field values distinctly (ie similar values select only ones) in one box (like combo box).
What kind of form or views can I use?
I don't really understand your question. Do you want to select distinct values for name, as in
Item.objects.values('name').distinct()
if you want to change a widget choices items, use something like this :
choices_list = Item.objects.values_list('name','name').distinct()
form_item = forms.ModelChoiceField(label = 'Select Item', choices = choices_list)
As said in the django field docs :
choices : An iterable (e.g., a list or tuple) of 2-tuples to use as choices for this field.