How make this special many2many fields validation using Django ORM?
I have the folowing model:
class Step(models.Model):
order = models.IntegerField()
latitude = models.FloatField()
longitude = models.FloatField()
date = DateField(blank=True, null=True)
class Journey(models.Model):
boat = models.ForeignKey(Boat)
route = models.ManyToManyField(Step)
departure = models.ForeignKey(Step, related_name="departure_of", null=开发者_如何学CTrue)
arrival = models.ForeignKey(Step, related_name="arrival_of", null=True)
I would like to implement the following check:
# If a there is less than one step, raises ValidationError.
routes = tuple(self.route.order_by("date"))
if len(routes) <= 1:
raise ValidationError("There must be at least two setps in the route")
# save the first and the last step as departure and arrival
self.departure = routes[0]
self.arrival = routes[-1]
# departure and arrival must at least have a date
if not (self.departure.date or self.arrival.date):
raise ValidationError("There must be an departure and an arrival date. "
"Please set the date field for the first and last Step of the Journey")
# departure must occurs before arrival
if not (self.departure.date > self.arrival.date):
raise ValidationError("Departure must take place the same day or any date before arrival. "
"Please set accordingly the date field for the first and last Step of the Journey")
I tried to do that by overloading save()
. Unfortunately, Journey.route
is empty in save()
. What's more, Journey.id
doesn't exists yet. I didn't try django.db.models.signals.post_save
but suppose it will fail because Journey.route
is empty as well (when does this get filled anyway?). I see a solution in django.db.models.signals.m2m_changed
but there are a lot of steps (thousands), and I want to avoid to perform an operation for every single of them.
If you're running the latest and greatest Django, have a look at this http://docs.djangoproject.com/en/dev/ref/models/instances/#id1
Basically, it's possible to validate Models in the same way as Forms. While I've never used it myself, it looks as a good fit to what you're trying to do.
Eventually, I had to write a validation for every form creating this object. knutin's solution would have been great but I run Django 1.1.
Anyway, Django let you overload the admin validation quite easily so I did :
class JourneyAdminForm(forms.ModelForm):
class Meta:
model = Journey
def clean_route(self):
"""
Ensure a Journey includes at least 2 dated steps,
departure starting before arrival.
"""
# must use getlist as self.data is not a dict but a querydict
routes = self.data.getlist("route")
if len(routes) <= 1:
raise ValidationError("There must be at least two setps in the route")
departure = Step.objects.get(id=routes[0])
arrival = Step.objects.get(id=routes[-1])
self.data["departure"] = departure.id
self.data["arrival"] = arrival.id
if not (departure.date and arrival.date):
raise ValidationError("There must be an departure and an arrival date. "
"Please set the date field for the first and last Step of the Journey")
if not (departure.date <= arrival.date):
raise ValidationError("Departure must take place the same day or any date before arrival. "
"Please set accordingly the date field for the first and last Step of the Journey")
return self.cleaned_data["route"]
class JourneyAdmin(admin.ModelAdmin):
exclude = ("arrival", "departure")
form = JourneyAdminForm
admin.site.register(Journey, JourneyAdmin)
As a bonus, ValidationError messages are displayed as a feedback for the user when submitting the form.
精彩评论