Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django : Validate data by querying the database in a model form (using custom clean method)

I am trying to create a custom cleaning method which look in the db if the value of one specific data exists already and if yes raises an error. I'm using a model form of a class (subsystem) who is inheriting from an other class (project). I want to check if the sybsystem already exists or not when i try to add a new one in a form.

I get project name in my view function.

class SubsytemForm(forms.ModelForm):  

    class Meta:
        model = Subsystem
        exclude = ('project_name')

    def clean(self,project_name):

        cleaned_data = super(SubsytemForm, self).clean(self,project_name)
        form_subsystem_name = cleaned_data.get("subsystem_name")

        Subsystem.objects.filter(project__project_name=project_name)
        subsystem_objects=Subsystem.objects.filter(project__project_name=project_name)
        nb_subsystem = subsystem_objects.count()

        for i in range (nb_subsystem):
            if (subsystem_objects[i].subsystem_name==form_subsystem_name):
                msg = u"Subsystem already existing"
                self._errors["subsystem_name"] = self.error_class([msg])

            # These fields are no longer valid. Remove them from the
            # cleaned data.
            del cleaned_data["subsystem_name"]
        return cleaned_data

My view function :

def addform(request,project_name):
    if form.is_valid(): 
        form=form.save(commit=False)
        form.project_id=Project.objects.get(project_name=project_name).id 
        form.clean(form,project_name)
        form.save()

This is not working and i don't know how to do. I have the error : clean() takes exactly 2 arguments (1 given)

My model :

class Project(models.Model):
project_name = models.CharField("Project name", max_length=20)

Class Subsystem(models.Model):
subsystem_name = models.Charfield("Subsystem name", max_length=20)
projects = models.ForeignKey(Project)
like image 960
user1507156 Avatar asked Dec 16 '22 21:12

user1507156


1 Answers

There are quite a few things wrong with this code.

Firstly, you're not supposed to call clean explicitly. Django does it for you automatically when you call form.is_valid(). And because it's done automatically, you can't pass extra arguments. You need to pass the argument in when you instantiate the form, and keep it as an instance variable which your clean code can reference.

Secondly, the code is actually only validating a single field. So it should be done in a specific clean_fieldname method - ie clean_subsystem_name. That avoids the need for mucking about with _errors and deleting the unwanted data at the end.

Thirdly, if you ever find yourself getting a count of something, iterating through a range, then using that index to point back into the original list, you're doing it wrong. In Python, you should always iterate through the actual thing - in this case, the queryset - that you're interested in. However, in this case that is irrelevant anyway as you should query for the actual name directly in the database and check if it exists, rather than iterating through checking for matches.

So, putting it all together:

class SubsytemForm(forms.ModelForm):  

    class Meta:
        model = Subsystem
        exclude = ('project_name')

    def __init__(self, *args, **kwargs):
        self.project_name = kwargs.pop('project_name', None)
        super(SubsystemForm, self).__init__(*args, **kwargs)

    def clean_subsystem_name(self):
        form_subsystem_name = self.cleaned_data.get("subsystem_name")

        existing = Subsystem.objects.filter(
                       project__project_name=self.project_name,
                       subsytem_name=form_subsystem_name
                   ).exists()

        if existing:
            raise forms.ValidationError(u"Subsystem already existing")

        return form_subsystem_name
like image 164
Daniel Roseman Avatar answered Apr 30 '23 22:04

Daniel Roseman