Guillaume Lebreton
Guillaume Lebreton

Reputation: 2773

How does Django CreateView class instanciate models?

I have a basic model / form / views:

models.py

class Tag(models.Model):
    name = models.CharField(max_length=100, unique=True)

forms.py

class TagForm(forms.ModelForm):
    class Meta:
        model = Tag
        fields = '__all__'

views.py

class TagCreate(CreateView):
    model = Tag
    form_class = TagForm
    success_url = reverse_lazy('view_somethin')

When using django-rest-frawework or Tag.objects.create(name="aagagaga"), the "name" field is passed to the __init__() of Tag, and i can do all hacks i want.

But when using the class based view, __init__() is of course called but no args are passed to. However, in the save method, i can check that the name attribute is already set to the good value.

My question is, how and where the data are transfered to the model instance if in case of class based views nothing is passed to the __init__() ?? I try to dig in the source code with no success, i suspect some king of manager

If you want to reproduce the issue, here is the testing code:

class Tag(models.Model):
    name = models.CharField(max_length=100, unique=True)

    def __str__(self):
        return f"Tag {self.name}"

    def __init__(self, *args, **kwargs):
        print(args)
        print(kwargs)
        super().__init__(*args, **kwargs)
        print(self.__dict__)

    def save(self, *args, **kwargs):
        print(self.__dict__)  # the filled attributes are here, but when were they filled ?
        super().save(*args, **kwargs)



Tag.objects.create(name="aagagaga")  # this call the __init__() with args as 
# intended but when using the html view, nothing is passed

Upvotes: 1

Views: 67

Answers (1)

Guillaume Lebreton
Guillaume Lebreton

Reputation: 2773

So i found a way, i don't exactly how it works internaly but i suppose Django modify the attributes of the model on the fly. Actually, the form stores a reference of the model instance in self.instance

So if you want to add/modify attributes like you would do in the __init__() of the model, you can do it by overriding form_valid of the CreateView:

class TagCreate(CreateView):
    model = Tag
    form_class = TagForm
    success_url = reverse_lazy('view_somethin')

    def form_valid(self, form):
        form.instance.new_attribute = "new_thing"
        return super().form_valid(form)

Then it appear in the model.

class Tag(models.Model):
    name = models.CharField(max_length=100, unique=True)

    def save(self, *args, **kwargs):
        print(self.__dict__)  # new_attribute appeared
        self.name += self.new_attribute
        super().save(*args, **kwargs)

Of course the new attribute is not meant to be saved in the database, but can be a useful modifier, like the current user.

Upvotes: 1

Related Questions