Home > Back-end >  Django - How to save model objects in another model?
Django - How to save model objects in another model?

Time:10-12

Let's say I have these two models (in models.py file):

class FullName (models.Model):
    firstName = models.CharField(max_length=30,null=True,blank=False,unique=True)
    lastName = models.CharField(max_length=30,null=True,blank=False,unique=True)

class Address (models.Model):
    addressLine = models.CharField(max_length=30,null=True,blank=False)
    city = models.CharField(max_length=30,null=True,blank=False)
    state = models.CharField(max_length=30,null=True,blank=False)
    zipcode = models.CharField(max_length=30,null=True,blank=False)

How can I merge these two models in a new model but with the same Id? So it becomes:

class FullName (models.Model):
    firstName = models.CharField(max_length=30,null=True,blank=False,unique=True)
    lastName = models.CharField(max_length=30,null=True,blank=False,unique=True)

class Address (models.Model):
    addressLine = models.CharField(max_length=30,null=True,blank=False)
    city = models.CharField(max_length=30,null=True,blank=False)
    state = models.CharField(max_length=30,null=True,blank=False)
    zipcode = models.CharField(max_length=30,null=True,blank=False)

class AllInformation (models.Model):
    firstName = models.CharField(max_length=30,null=True,blank=False,unique=True)
    lastName = models.CharField(max_length=30,null=True,blank=False,unique=True)
    addressLine = models.CharField(max_length=30,null=True,blank=False)
    city = models.CharField(max_length=30,null=True,blank=False)
    state = models.CharField(max_length=30,null=True,blank=False)
    zipcode = models.CharField(max_length=30,null=True,blank=False)

    (all fields should be inherited from FullName and Address models)

PS: In my views.py file I called a save method like that:

fullNameData = FullName(request.POST)
fullNameData.save()
        and
AddressData = Address(request.POST)
AddressData.save()

Thanks in advance and I really appreciate who answers that because I see that many people having the same problem. I also took a look at the OneToOneField from django docs but I understood nothing to be honest xD

CodePudding user response:

A One-To-One would probably be the way to go, it's just a ForeignKey so you don't have duplicate data in the DB

Your Model would look like this

class AllInformation(models.Model):
    fullname = models.OneToOneField(FullName, on_delete=models.CASCADE)
    address = models.OneToOneField(Address, on_delete=models.CASCADE)

And then your save method in the view would look something like this:

# save the full name
fullNameData = FullName(request.POST)
fullname = fullNameData.save()

# save the address
AddressData = Address(request.POST)
address = AddressData.save()

# create the information 'connector'
informationObj = AllInformation.objects.create(fullname=fullname, address=address) 

# example uses
informationObj.fullname.firstName 
informationObj.fullname.lastName 
informationObj.address.city 

Personally I'd just combine all three into a single form like:

forms.py
from django import forms
#   import all 3 models

# Note: NOT a model form, this is Custom Form so it can handle more than 1 model
class InformationForm(forms.Form):

    # I Made some fields required so a form submit will always create both a FullName   Address Object
    # - else it's extra code to handle that   I assume you want both anyways

    firstName = forms.CharField(required=True)
    lastName = forms.CharField(required=True)

    addressLine = forms.CharField(required=True)
    city = forms.CharField()
    state = forms.CharField()
    zipcode = forms.CharField()

    def __init__(self,  *args, **kwargs):
        # Holds Obj if it's an Edit Form (needs to pop here as super() doesn't like it)
        self.instance = kwargs.pop('instance') if 'instance' in kwargs else None

        super(InformationForm, self).__init__(*args, **kwargs)

        if self.instance:
            # is an edit form, set initial fields
            self.initial = {
                'firstName': instance.fullname.firstName, 
                'lastName': instance.fullname.lastName, 
                'addressLine': instance.address.addressLine,
                'city': instance.address.city,
                'state': instance.address.state,
                'zipcode': instance.address.zipcode,
            }

        # Add HTML attributes / HTML Validation
        self.fields['firstName'].widget.attrs={'maxlength': '30'}
        self.fields['lastName'].widget.attrs={'maxlength': '30'}

        self.fields['addressLine'].widget.attrs={'maxlength': '30'}
        self.fields['city'].widget.attrs={'maxlength': '30'}
        self.fields['state'].widget.attrs={'maxlength': '30'}
        self.fields['zipcode'].widget.attrs={'maxlength': '30'}

    def is_valid(self):
        valid = super(InformationForm, self).is_valid()

        # Check for Duplicates!

        if FullName.objects.filter(firstName= self.cleaned_data.get('firstName'), lastName= self.cleaned_data.get('lastName')).first():
            self.add_error('firstName', 'FullName Already Exists')
            self.add_error('lastName', 'FullName Already Exists')
            valid = False

        if Address.objects.filter(
            addressLine = self.cleaned_data.get('addressLine'),
            city = self.cleaned_data.get('city'),
            state = self.cleaned_data.get('state'),
            zipcode = self.cleaned_data.get('zipcode')
            ).first():
            self.add_error('addressLine', 'Address Already Exists')
            self.add_error('city', 'Address Already Exists')
            self.add_error('state', 'Address Already Exists')
            self.add_error('zipcode', 'Address Already Exists')
            valid = False
        return valid

    def save(self):
        # There is no .save(commit=False) method with this form.

        if self.instance:
            # is an edit form

            self.instance.fullname.firstName = self.cleaned_data.get('firstName'),
            self.instance.fullname.lastName = self.cleaned_data.get('lastName'),
            self.instance.fullname.save()

            self.instance.address.addressLine = self.cleaned_data.get('addressLine'),
            self.instance.address.city = self.cleaned_data.get('city'),
            self.instance.address.state = self.cleaned_data.get('state'),
            self.instance.address.zipcode = self.cleaned_data.get('zipcode'),
            self.instance.address.save()

            return self.instance
        else:
            # is a new form

            fullNameObj = FullName.objects.create(
                firstName = self.cleaned_data.get('firstName'),
                lastName = self.cleaned_data.get('lastName'),
            )
            addressObj = Address.objects.create(
                addressLine = self.cleaned_data.get('addressLine'),
                city = self.cleaned_data.get('city'),
                state = self.cleaned_data.get('state'),
                zipcode = self.cleaned_data.get('zipcode'),
            )

            informationObj = AllInformation.objects.create(
                fullname=fullNameObj, address=addressObj
            ) 
        return informationObj
views.py
from django.shortcuts import render, redirect, get_object_or_404
#   import form & AllInformation

def informationFormView(request, pk=None):

    # This being None = New Form
    informationObj = None

    if pk:
        informationObj = get_object_or_404(AllInformation, pk=pk)

    infoform = InformationForm(data=request.POST or None, instance=informationObj)

    if request.method == 'POST':
        if infoform.is_valid():
            infoform.save()

            redirect('success')

        # on not valid it'll re-render the page with errors 

    data = {
        'obj': informationObj
        'form': infoform,
    }
    return render(request, 'informationForm.html', data)
urls.py
from django.urls import path
from . import views

urlpatterns = [

    # Two URLs -> 1 View
    path('informationform', views.informationFormView, name='informationFormView'),
    path('informationform/<int:pk>', views.informationFormView, name='informationFormView'),

    ]
informationForm.html
  • Basic form ripped from Docs, changed the Action to handle edits
<form action="{% if obj %}{% url 'informationFormView' pk=obj.pk %}{% else %}{% url 'informationFormView' %}{% endif %}" method="post">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="Submit">
</form>
  • Related