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>