Django Admin - How to pull model default field val

2019-09-11 01:37发布

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

标签: django admin
2条回答
Ridiculous、
2楼-- · 2019-09-11 02:15

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.

查看更多
Anthone
3楼-- · 2019-09-11 02:16

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.

查看更多
登录 后发表回答