58

I have a very simple Django app in order to record the lectures given my colleagues.Since it is quite elementary,I am using the Django admin itself. Here is my models.py:

#models.py
from django.db import models

class Lecture(models.Model):
    topic = models.CharField(max_length=100)
    speaker = models.CharField(max_length=100)
    start_date = models.DateField()
    end_date = models.DateField()

I need to ensure that nobody enters the start date after the end date in the admin forms,so I read the django docs for custom validation in the admin and implemented the following in my admin.py:

#admin.py
from models import Lecture
from django.contrib import admin
from django import forms


class LectureForm(forms.ModelForm):
    class Meta:
        model = Lecture

        def clean(self):
            start_date = self.cleaned_data.get('start_date')
            end_date = self.cleaned_data.get('end_date')
            if start_date > end_date:
                raise forms.ValidationError("Dates are incorrect")
        return self.cleaned_data


class LectureAdmin(admin.ModelAdmin):
    form = LectureForm
    list_display = ('topic', 'speaker', 'start_date', 'end_date')

admin.site.register(Lecture, LectureAdmin)

However,this has no effect whatsoever on my admin and I am able to save lectures where start_date is after end_date as seen in the image:enter image description here

What am I doing wrong ??

rynmrtn
  • 3,371
  • 5
  • 28
  • 44
Amistad
  • 7,100
  • 13
  • 48
  • 75

3 Answers3

70

Usually you just want to define a clean() method on the model itself.

https://docs.djangoproject.com/en/2.1/ref/models/instances/#validating-objects

from django.core.exceptions import ValidationError

class Lecture(models.Model):
    topic = models.CharField(max_length=100)
    speaker = models.CharField(max_length=100)
    start_date = models.DateField()
    end_date = models.DateField()

    def clean(self):
        if self.start_date > self.end_date::
            raise ValidationError("Dates are incorrect")

Something like that will work in the django admin without any need to create a form class.

aris
  • 22,725
  • 1
  • 29
  • 33
  • In my case although error is raised, object is still able to be saved even after error raised – Rafał Nov 23 '20 at 13:56
  • 3
    Keep in mind that a model's `clean()` method isn't called automatically with `save()`. This works in the Admin page because its forms call the `clean()` method for you. You'll still need to call `clean()` before `save()` elsewhere. From the docs: ["Note, however, that like Model.full_clean(), a model’s clean() method is not invoked when you call your model’s save() method."](https://docs.djangoproject.com/en/dev/ref/models/instances/#django.db.models.Model.clean) – ChrisCrossCrash Mar 31 '21 at 10:24
  • Worth to note that changing the error raised by `ValidationError({"field_name": "Dates are incorrect"})`, the message will be displayed along the field named "field_name". Also adding more `"field": "message error .."` pairs to the dict will allow to add more errors specific to each field. – Mariano Ruiz Feb 09 '22 at 19:39
42

You have an indentation issue. Your clean method is indented within the form's Meta class. Move it back one level. Also, ensure that the return statement is indented within the method.

Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • that worked !!..and I almost spent 2 hours re reading the docs to get a fix for it !!..I had one more doubt..the validation error was thrown up only when i created a new entry.For the existing ones,they continued to be there..I am assuming that the clean() method is called only when a model is saved.How do i ensure that it is called every time the page loads ? – Amistad Jul 17 '14 at 11:36
  • 1
    @Amistad The `clean` method is called only when the form is submitted, not "when the model is saved" (if that's what you want you're looking for model's validation: https://docs.djangoproject.com/en/1.7/ref/models/instances/#validating-objects)... Which will still not validate your model instances "every time the page loads", but why would you want such a "feature" anyway ? – bruno desthuilliers Jul 17 '14 at 11:48
  • hmm..now that I think of it,all my changes to the model happen only via the admin through the form..so a validation check there should suffice..thanks.. – Amistad Jul 17 '14 at 11:54
  • @bruno..when i use start_date and end_date in the list editable in the change list view and try changing the dates there,the form validation does not kick in..How do i ensure that form validation is performed while performing changes via the list editable option ?? – Amistad Jul 17 '14 at 12:32
  • 2
    You can define the [`get_changelist_form`](https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_changelist_form) method to return a custom form to use there. – Daniel Roseman Jul 17 '14 at 12:37
  • Fixed link for @brunodesthuilliers comment: https://docs.djangoproject.com/en/1.9/ref/models/instances/#validating-objects – mplewis Mar 25 '16 at 02:22
  • 1
    Just in case it can help other people: Note that it's important to import "from django.forms import ValidationError" NOT "from django.core.exceptions import ValidationError" – jobima Jun 30 '16 at 11:28
0
def clean(self): 
   if self.database_name != '111':
      raise ValidationError("You can use only letters ,numbers and 
      underscores.")

This clean method works perfect for me in django admin