0

I am using CreateView to update my Person model. That model has a onetoone relation with Adress. When trying to update my Person model I cannot update the Adress model at the same time. It just gives me a list of existing addresses, but no way to add an new address. I can see that this is possible in admin. is it possible to this in th CreateView?

model.py:

 class Person(Model):
    
        fname=CharField(default="missing",max_length=100)
        lname=CharField(default="missing",max_length=100)
        mobil=PhoneField(default='9999999999')
        mail=EmailField(default='contact@gmail.com')
        padress=OneToOneField(Adress,on_delete=CASCADE,primary_key=True)
    
        def __str__(self):
            return self.fname
    
        class Meta:
            ordering=('fname','lname')
    
    class Adress(Model):
    
        street=CharField(default='missing',max_length=100)
        snumb=CharField(default='missing',max_length=15)
        town=CharField(default='missing',max_length=100)
        postn=CharField(default='99999',max_length=5,validators=[postnvali])
        
        def __str__(self):
            return self.town
    
        class Meta:
            ordering=('street','town')

view.py

    class PCreate(CreateView):
        
        template_name='kammem/create.html'
        model=Person
        fields=['fname','lname','mobil','mail','padress']
        success_url=reverse_lazy('forl')

url.py

    path('pcreate/',PCreate.as_view(),name='pcreate'),

template: 

    {% extends 'edit2.html' %}
    
    {% block add %}
    <form method="post">{% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="Save">
    </form>
    {% endblock %}

any suggestions?

ok, here is an update. I can save both instances and also create the relation. But I still can't exit the function, please look at the following:

class PCreate2(CreateView):
    
    form_class=PersonForm
    template_name='kabmmem/create.html'
    success_url=reverse_lazy('forl')

    def form_valid(self,form):
        # create address and person
        street = form.cleaned_data['street']
        snumb = form.cleaned_data['snumb']
        town = form.cleaned_data['town']
        postn = form.cleaned_data['postn']
        fname = form.cleaned_data['fname']
        lname = form.cleaned_data['lname']
        mobil = form.cleaned_data['mobil']
        mail = form.cleaned_data['mail']
        a=Adress.objects.create(street=street,snumb=snumb,town=town,postn=postn)
        pe=Person.objects.create(fname=fname,lname=lname,mobil=mobil,mail=mail,padress=a)

        return HttpResponseRedirect(self.get_success_url())

The return statement gives: AttributeError: 'NoneType' object has no attribute 'dict'

so, I am still lost...

the return statement

  • Why not move the fields from `Adress` to `Person`? Using a `OneToOneField` when it's not necessary can lead to these complications – Iain Shelvington Jan 31 '21 at 09:33
  • I know, but there are other models that use adresses, like a venue for example. so I thought it was best to have a separate model for that. – Jonas Fredriksson Jan 31 '21 at 09:37

2 Answers2

0

You can setup a PersonForm ModelForm and define the address fields within it. Assigning this form to the form_class attribute of the view would ensure the address fields are available for editing on the page.

After the form has been validated, you would have to manually create the address object and assign it to the padress field of the form. You can do this in the form_valid method of the view.

forms.py

from django import forms

class PersonForm(forms.ModelForm):
    street=forms.CharField(max_length=100)
    snumb=forms.CharField(max_length=15)
    town=forms.CharField(max_length=100)
    postn = forms.CharField(max_length=5, validators=[postnvali])

    class Meta:
        model = Person
        exclude = ['padress']

views.py

class PCreate(CreateView):
    
    form_class=PersonForm
    template_name='kammem/create.html'
    success_url=reverse_lazy('forl')

    def form_valid(self, form):
        # create address
        street = form.cleaned_data['street']
        snumb = form.cleaned_data['snumb']
        town = form.cleaned_data['town']
        postn = form.cleaned_data['postn']
        address = Address.objects.create(street=street, snumb=snumb, town=town, postn=postn)

        # get the response
        response = super().form_valid(form)

        # assign the address to the person object
        self.object.padress = address
        self.object.save()

        # return the response
        return response
JOSH IK
  • 66
  • 1
  • 3
  • thanks. I got a NOT NULL constraint failed: kammem_person.padress_id error. but it might be the right way to it like this – Jonas Fredriksson Jan 31 '21 at 22:32
  • another thing, is this the way it is solved in the admin site? – Jonas Fredriksson Feb 01 '21 at 10:20
  • My bad, I have updated the answer to fix the NOT NULL error. – JOSH IK Feb 04 '21 at 08:06
  • The admin site uses the RelatedFieldWidgetWrapper widget to display a plus sign that opens a pop up with a form for the creating the related model. More [here](https://stackoverflow.com/a/13167827/5056632). – JOSH IK Feb 04 '21 at 08:08
  • The admin site can also create the related model on the same page(inline) as the parent model by using the [InlineModelAdmin](https://docs.djangoproject.com/en/3.0/ref/contrib/admin/#django.contrib.admin.InlineModelAdmin). This is based on formsets. More [here](https://swapps.com/blog/working-with-nested-forms-with-django/) – JOSH IK Feb 04 '21 at 08:09
  • still NOT NULL constraint failed: kammem_person.padress_id even when I tried self.object.padress_id = address.id – Jonas Fredriksson Feb 04 '21 at 09:16
  • I have updated the code but Still problems. It is strange, updating a relational db should be straight forward. – Jonas Fredriksson Feb 04 '21 at 17:06
0

ok, so finaly after a lot of research found a solution. I simply set the two tables in the CreateView function and return the succes url. It is an ugly solution. I anyone knows any better solution, please let me know. It is such a common pattern to update foreign fields but the documentation i really sparse when it comes to this,

views.py:

class PCreate2(CreateView):
    
    form_class=PersonForm
    template_name='kammem/create.html'

    def form_valid(self,form):
        # create address
        street = form.cleaned_data['street']
        snumb = form.cleaned_data['snumb']
        town = form.cleaned_data['town']
        postn = form.cleaned_data['postn']
        fname = form.cleaned_data['fname']
        lname = form.cleaned_data['lname']
        mobil = form.cleaned_data['mobil']
        mail = form.cleaned_data['mail']
        a=Adress.objects.create(street=street,snumb=snumb,town=town,postn=postn)
        pe=Person.objects.create(fname=fname,lname=lname,mobil=mobil,mail=mail,padress=a)

        return HttpResponseRedirect(reverse_lazy('personer'))