今天我遇到了一个我认为只能用Django解决的问题。
我有以下admin.py:
# encoding: utf-8
from django.contrib import admin
from django import forms
from django.utils.translation import ugettext_lazy as _
from . models import Service, ServiceItem, Optional, \
Subscription, SubscriptionItem, SubscriptionConsumption
class ServiceItemInline(admin.StackedInline):
model = ServiceItem
fields = ('optional', 'price', 'price_excess', 'currency',
'start_date', 'account',
'country', 'state', 'region', 'city')
extra = 1
class ServiceAdmin(admin.ModelAdmin):
fields = ('name', 'short_name', 'unit', 'optionals')
inlines = [ServiceItemInline, ]
class BaseSubscriptionFormSet(forms.models.BaseInlineFormSet):
def clean(self):
super(BaseSubscriptionFormSet, self).clean()
total_consumption = 0.0
total_burst = 0.0
for form in self.forms:
if not form.is_valid():
return
if form.cleaned_data:
if not form.cleaned_data['optional']:
total_consumption = total_consumption + \
form.cleaned_data['consumption_value']
total_burst = total_burst + \
form.cleaned_data['burst_value']
# Raises error if values are not the same
if total_consumption != self.instance.consumption_value:
raise forms.ValidationError(_('Sum of all Consumption values '
'must be same as '
'defined in Subscription.'))
if total_burst != self.instance.burst_value:
raise forms.ValidationError(_('Sum of all Burst values '
'must be same as '
'defined in Subscription.'))
class ServiceConsumptionInline(admin.TabularInline):
model = SubscriptionConsumption
fields = ('optional', 'consumption_value', 'burst_value', 'burst_interval')
class SubscriptionContractInline(admin.StackedInline):
model = SubscriptionItem
formset = BaseSubscriptionFormSet
fields = ('optional', 'price', 'price_excess', 'currency',
'start_date', 'account',
'country', 'state', 'region', 'city',
'consumption_value', 'burst_value', 'burst_interval',
'end_date', 'is_active')
class SubscriptionAdmin(admin.ModelAdmin):
inlines = [ServiceConsumptionInline, SubscriptionContractInline, ]
admin.site.register(Optional)
admin.site.register(Service, ServiceAdmin)
admin.site.register(Subscription, SubscriptionAdmin)
我的SubscriptionAdmin有2个内联。我想比较这些内联。如果没有Javascript,它甚至可以吗?
这里更清楚的是我的models.py:
# encoding: utf-8
import datetime
from django.db import models
from django.core.validators import MinValueValidator
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from apps.account.models import (
Account, Country, Currency
)
from apps.client.models import Client
class Item(models.Model):
price = models.FloatField(validators=[MinValueValidator(0.0)],
help_text=_('Unit price'))
price_excess = models.FloatField(validators=[MinValueValidator(0.0)],
help_text=_('Unit price for excess.'))
currency = models.ForeignKey(Currency)
account = models.ForeignKey(Account)
country = models.ForeignKey(Country, null=True, blank=True)
state = models.CharField(max_length=250, blank=True)
region = models.CharField(max_length=250, blank=True)
city = models.CharField(max_length=250, blank=True)
start_date = models.DateField(help_text=_('Subscription start date item.'))
class Meta:
abstract = True
class Optional(models.Model):
UNIT_CHOICES = (
(1, 'Giga Bytes'),
(2, 'Hits'),
(3, 'Counter'),
(4, 'Feature'),
)
name = models.CharField(
max_length=250, help_text=_('e.g. SSL Custom Domain.'))
unit = models.PositiveSmallIntegerField(max_length=250,
choices=UNIT_CHOICES)
def __unicode__(self):
return u'{0} ({1})'.format(self.name, self.get_unit_display())
class Service(models.Model):
UNIT_CHOICES = (
(1, 'Giga Bytes'),
)
SHORT_NAME_CHOICES = (
(1, 'PH'),
(2, 'HS'),
(3, 'LS'),
(4, 'HC'),
)
name = models.CharField(max_length=250,
help_text=_('e.g. HTTP Caching'))
short_name = models.PositiveSmallIntegerField(max_length=2,
unique=True,
choices=SHORT_NAME_CHOICES)
unit = models.PositiveSmallIntegerField(max_length=250,
choices=UNIT_CHOICES)
optionals = models.ManyToManyField(Optional)
def __unicode__(self):
return self.name
class ServiceItem(Item):
service = models.ForeignKey(Service)
optional = models.ForeignKey(Optional, null=True, blank=True)
created_by = models.ForeignKey(Account,
null=True,
blank=True,
related_name='services')
def __unicode__(self):
return self.service.name
def clean(self):
# Checks if this object already exists.
# TODO: refactor this unique value validation.
qs = ServiceItem.objects.exclude(id=self.id)
qs = qs.filter(service=self.service,
optional=self.optional,
currency=self.currency,
account=self.account,
country=self.country,
state=self.state,
region=self.region,
city=self.city,
start_date=self.start_date)
if qs.exists():
raise ValidationError(_('Service Item should be unique.'))
def get_absolute_url(self):
return reverse('service:service_item_edit', args=[self.id])
class Subscription(models.Model):
service = models.ForeignKey(Service)
client = models.ForeignKey(Client)
consumption_value = models.FloatField(
validators=[MinValueValidator(0.0)],
default=0,
help_text=_('Max value for consumption, after that produces excess.'))
burst_value = models.FloatField(
validators=[MinValueValidator(0.0)],
default=0,
help_text=_('Max burst value, after that produces an excess.'))
burst_interval = models.FloatField(
validators=[MinValueValidator(0.0)],
default=0,
help_text=_('Interval of burst value in seconds.'))
def __unicode__(self):
return self.service.name
class SubscriptionConsumption(models.Model):
subscription = models.ForeignKey(Subscription)
optional = models.ForeignKey(Optional)
consumption_value = models.FloatField(
validators=[MinValueValidator(0.0)],
default=0,
help_text=_('Max value for consumption, after that produces excess.'))
burst_value = models.FloatField(
validators=[MinValueValidator(0.0)],
default=0,
help_text=_('Max burst value, after that produces an excess.'))
burst_interval = models.FloatField(
validators=[MinValueValidator(0.0)],
default=0,
help_text=_('Interval of burst value in seconds'))
class Meta:
unique_together = ('optional', 'subscription', )
def __unicode__(self):
return self.subscription.service.name
class SubscriptionManager(models.Manager):
def with_optionals(self):
return self.filter(optional__isnull=False)
def without_optionals(self):
return self.filter(optional__isnull=True)
class SubscriptionItem(Item):
subscription = models.ForeignKey(Subscription)
optional = models.ForeignKey(Optional, null=True, blank=True)
objects = SubscriptionManager()
consumption_value = models.FloatField(
validators=[MinValueValidator(0.0)],
default=0,
null=True,
blank=True,
help_text=_('Max value for consumption, after that produces excess.'))
burst_value = models.FloatField(
validators=[MinValueValidator(0.0)],
default=0,
null=True,
blank=True,
help_text=_('Max burst value, after that produces an excess.'))
burst_interval = models.FloatField(
validators=[MinValueValidator(0.0)],
default=0,
null=True,
blank=True,
help_text=_('Interval of burst value in seconds'))
end_date = models.DateField(
null=True,
blank=True,
help_text=_('After hitting end date, subscription will no '
'longer be active.'))
is_active = models.BooleanField(
_('Active'), default=True,
help_text=_('If your subscription is deactivated, '
'it will no longer work.'))
class Meta:
unique_together = (('subscription', 'optional',
'account', 'country',
'state', 'region', 'city', 'start_date'))
def __unicode__(self):
return self.subscription.service.name
def clean(self):
# Checks if this subscription belongs to Service settings.
if self.optional:
if self.optional not in self.subscription.service.optionals.all():
raise ValidationError(
_('This optional is not available for this subscription.'))
def is_expired(self):
"""Returns True if Subscription has end_date
less than today or if is_active is True."""
diff = datetime.datetime.utcnow() - self.end_date.replace(tzinfo=None)
if diff > 0:
return True
else:
return False
用例:
添加SubscriptionItem时,我应该能够将ServiceConsumptionInline与SubscriptionContractInline进行比较,执行一些计算并检查值是否正常。如果他们对我的业务没问题就会引发ValidationError。
我做了很多搜索,但我没有发现类似的问题。我尝试了IRC但没有成功。
答案 0 :(得分:0)
formset有一个类似于Form类的方法。这个 是您在formset上定义自己的验证的地方 水平。
请注意,两个内联表单集prefix用于具有给定值的formset表单字段名称,以允许将多个表单集发送到视图而不会发生名称冲突。