Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: Only one of two fields can be filled in

I have this model:

class Journals(models.Model):
    jid = models.AutoField(primary_key=True)
    code = models.CharField("Code", max_length=50)
    name = models.CharField("Name", max_length=2000)
    publisher = models.CharField("Publisher", max_length=2000)
    price_euro = models.CharField("Euro", max_length=2000)
    price_dollars = models.CharField("Dollars", max_length=2000)

Is there a way to let people fill out either price_euro or price_dollars?

I do know that the best way to solve the problem is to have only one field and another table that specify the currency, but I have constraints and I cannot modify the DB.

Thanks!

like image 996
Giovanni Di Milia Avatar asked Apr 27 '10 18:04

Giovanni Di Milia


People also ask

Can I have two primary keys in Django?

Do Django models support multiple-column primary keys? ¶ No. Only single-column primary keys are supported.

What is null True in Django?

null = True. Means there is no constraint of database for the field to be filled, so you can have an object with null value for the filled that has this option. blank = True. Means there is no constraint of validation in django forms.

What is form instance in Django?

Bound and unbound forms A Form instance is either bound to a set of data, or unbound. If it's bound to a set of data, it's capable of validating that data and rendering the form as HTML with the data displayed in the HTML.

What are Django fields?

Fields in Django are the data types to store a particular type of data. For example, to store an integer, IntegerField would be used. These fields have in-built validation for a particular data type, that is you can not store “abc” in an IntegerField.


4 Answers

Since Django 2.2 database constraints can be used. That means, that on top of the regular model-level validation (which has worked since forever and has been suggested by Tom) you can now make sure that invalid data cannot enter the database at all.

# models.py 
from django.db import models
from django.db.models import Q

class Journals(models.Model):
    # [...]
    
    def clean(self):
        """Ensure that only one of `price_euro` and `price_dollars` can be set."""
        if self.price_euro and self.price_dollars:
            raise ValidationError("Only one price field can be set.")


    class Meta:
        constraints = [
            models.CheckConstraint(
                check=(
                    Q(price_euro__isnull=False) & 
                    Q(price_dollars__isnull=True)
                ) | (
                    Q(price_euro__isnull=True) & 
                    Q(price_dollars__isnull=False)
                ),
                name='only_one_price',
            )
        ]

Make sure to create and apply a migration after Meta.constraints has been changed. Documentation on Meta.constraints is available here.

like image 64
jnns Avatar answered Nov 07 '22 08:11

jnns


It's not necessary that the interface reflects the data structure. You could simply have your UI present them with 1 currency field and then a selection between USD and Euro, then when the data is committed back to the Journals model, you simply assign it to the correct field and clear the other.

like image 32
T. Stone Avatar answered Nov 07 '22 08:11

T. Stone


I'd agree with T. Stone that customizing the form is the best way to go. If you can't do that, or in addition to that, you can enforce the rule with a custom clean() method on your form. Just bear in mind that any validation errors you throw in the clean() method won't be attached to specific fields, but to the form itself.

like image 45
Tom Avatar answered Nov 07 '22 09:11

Tom


I would do it on the model clean() method. That way it will work with the Admin site as well.

https://docs.djangoproject.com/en/dev/ref/models/instances/#django.db.models.Model.clean

like image 1
wobbily_col Avatar answered Nov 07 '22 08:11

wobbily_col