I am trying to get a field value automatically pulled from different table. Below is my case: I have two tables: sales and returns. While entering a new return, when a "sales_id" is selected, want to show "sales_quantity" automatically populated, non-editable (and also if possible, want to constraint the "return_quantity" <= "sales_quantity").
class T_Sales(models.Model):
product = models.ForeignKey(P_Product)
sales_quantity = models.IntegerField()
def __unicode__(self):
return str(self.id)
class T_Return(models.Model):
sales_id = models.ForeignKey(T_Sales)
#sales_quantity = models.IntegerField(default=T_Sales.sales_quantity)
return_quantity = models.IntegerField()
def __unicode__(self):
return self.description
Creating property in your model will do the job. sales_id
won't be null, so there will never be an issue when trying to get that value.
class T_Sales(models.Model):
product = models.ForeignKey(P_Product)
sales_quantity = models.IntegerField()
def __unicode__(self):
return str(self.id)
class T_Return(models.Model):
sales_id = models.ForeignKey(T_Sales)
return_quantity = models.IntegerField()
def __unicode__(self):
return self.description
@property
def sales_quantity(self):
return self.sales_id.sales_quantity
Pros of that method:
- value is not stored in your database twice
- value will be automatically updated if there is change on
T_Sales
object.
Cons of that method:
- It will trigger separate query when fetching that field, unless use
select_related
in your queryset.
- it is hard (but possible) to print that field in detail view inside django admin (you can always show it only on list).
If you wan't to create some validation, you can override model's clean
method and do your comparsion here, if there is something wrong, you should throw ValidationError
. Example:
from django.core.exceptions import ValidationError
class T_Return(models.Model):
# ......
def clean(self):
if self.return_quantity > self.sales_quantity:
raise ValidationError("You can't return more than it was bought")
return super(T_Return, self).clean()
You can also assign error to return_quantity
field, just pass dict in form 'field_name': error
into validation error:
raiseValidationError({'return_quantity': _('You can't return more than it was bought.')}
You can't assign that error to sales_quantity
.
You can create separate field in T_Return
model and copy value from T_Sales
on save:
class T_Sales(models.Model):
product = models.ForeignKey(P_Product)
sales_quantity = models.IntegerField()
def __unicode__(self):
return str(self.id)
class T_Return(models.Model):
sales_id = models.ForeignKey(T_Sales)
sales_quantity = models.IntegerField(editable=False)
return_quantity = models.IntegerField()
def __unicode__(self):
return self.description
def save(self, *args, **kwargs):
if not self.sales_quantity:
self.sales_quantity = self.sales_id.sales_quantity
supr(T_Return, self).save(*args, **kwargs)
Pros of that method:
- It will trigger additional query to
T_Sales
only on (first) save.
- It is easy to display value in admin's detail view
Cons of that method:
- You're storing value in database twice
- If value in T_Sales
object will change, value in T_Return
won't be changed automatically (that can be fixed by simple trigger on save of T_Sales
, but only inside django ORM)
If you wan't to create some validation, you can override model's clean
method and do your comparsion here, if there is something wrong, you should throw ValidationError
. Example:
from django.core.exceptions import ValidationError
class T_Return(models.Model):
# ......
def clean(self):
if self.return_quantity > self.sales_quantity:
raise ValidationError("You can't return more than it was bought")
return super(T_Return, self).clean()
You can also assign error to return_quantity
or to sales_quantity
field, just pass dict in form 'field_name': error
into validation error:
raiseValidationError({'return_quantity': _('You can't return more than it was bought.')}
Assigning that errot both to return_quantity
and sales_quantity
will show that error twice.