Calvin Lin
Calvin Lin

Reputation: 97

Allowing a single form to update 2 tables at once

I'm having a trouble where I want to use a form to update 2 different tables. My current form have a few variables. Hostname, ipaddr, mgmt, mgmtip etc. What I want to do now is take hostname, ipaddr and add into one table (This one done) and the other 2, mgmt and mgmtip to another table while under the same id. Like for example, hostname and ipaddr is registered under id of 1 (1st row of table 1) and mgmt and mgmtip to table 2 of the same id = 1. I am currently also able to extract the id for the 1st table. But I just cant seem to update the table2 under the same id.

Below is my code: In views.py

def device_add(request):

if request.method == "POST":
    device_frm = DeviceForm(request.POST) ##Part A1
    dd_frm = DeviceDetailForm()
    if device_frm.is_valid():
        device_frm.save()  ##Part A1
        deviceid = Device.objects.aggregate(Max('id')) ##Part A1 - Getting the ID of added device 
        device = Device.objects.all() ##Part A1
        if dd_frm.is_valid():
            dd_frm.save('id' == deviceid)
    return render(request, 'interface/device-added.html',{'devices':device})
       

else:
    device_frm = DeviceForm()
    dd_frm = DeviceDetailForm()
    di_frm = DeviceInterfaceForm()
    return render(request,'interface/device_add.html',{'form':device_frm, 'dd_form': dd_frm})

In models.py

class DeviceDetail(models.Model):
    hostname = models.CharField(max_length=50)
    mgt_interface = models.CharField(max_length=50)
    mgt_ip_addr = models.CharField(max_length=30)
    subnetmask = models.CharField(max_length=30)
    ssh_id = models.CharField(max_length=50)
    ssh_pwd = models.CharField(max_length=50)
    enable_secret = models.CharField(max_length=50)
    device_model = models.CharField(max_length=50)
    
    def __str__(self):
        return self.hostname

In forms.py

class DeviceDetailForm(ModelForm):
    class Meta:
        model= DeviceDetail
        fields= ['hostname', 'mgt_interface', 'mgt_ip_addr', 'subnetmask', 'ssh_id', 'ssh_pwd', 'enable_secret', 'device_model']
    def clean(self):
        cleaned_data = super(DeviceDetailForm, self).clean()
        hostname = cleaned_data.get('hostname')
        if len(hostname) < 8:
            raise forms.ValidationError('Hostname needs to be more than 8 character long, ' + hostname )

Upvotes: 1

Views: 77

Answers (2)

danial hadi
danial hadi

Reputation: 69

if you want to relative two tables together you must use relative fields in your model, like ForeignKey, ManyToManyField, and ...,

model:

class DeviceModel(Model.models):
    hostname = models.CharField(max_length=100)
    ipaddr = models.CharField(max_length=50)

class DeviceDetailModel(Model.models):
    device = models.ForeignKey(DeviceModel, related_name='Device_Model', on_delete=models.CASCADE)
    mgmt = models.SOMEField
    mgmtip  = models.SOMEField

Form:

from django import forms
from .models import DeviceModel, DeviceDetailModel
    class DeviceForm(forms.ModelForm):
        class Meta:
            model = DeviceModel
            fields = '__all__'
    class DeviceDetailForm(forms.ModelForm):
        mgmt = forms.SOMEField
        mgmtip  = forms.SOMEField
        class Meta:
            model = DeviceDetailModel
            fields = '__all__'

and now

if request.method == "POST":
    device_frm = DeviceForm(request.POST)
    dd_frm = DeviceDetailForm(request.POST)
    if device_frm.is_valid():
        device_frm.save()  ##Part A1
        deviceid = Device.objects.aggregate(Max('id')) 
        device = Device.objects.all() ##Part A1
        if dd_frm.is_valid():
            deviceD = dd_frm.save(commit=False)
            deviceD.device = Device.objects.get(id=deviceid)
            deviceD.save()
    return render(request, 'interface/device-added.html',{'devices':device})

P.S: you forget to add request.POST to dd_frm = DeviceDetailForm(request.POST)

Upvotes: 1

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476594

Your model should work with a ForeignKey or OneToOneField to refer to the device it is linked to, so:

class DeviceDetail(models.Model):
    # …
    device_model = models.ForeignKey(Device, on_delete=models.CASCADE)

In your form, you should return the cleaned data after validation, so:

class DeviceDetailForm(ModelForm):
    class Meta:
        model= DeviceDetail
        fields= ['hostname', 'mgt_interface', 'mgt_ip_addr', 'subnetmask', 'ssh_id', 'ssh_pwd', 'enable_secret', 'device_model']

    def clean(self):
        cleaned_data = super().clean()
        hostname = cleaned_data.get('hostname')
        if len(hostname) < 8:
            raise forms.ValidationError(f'Hostname needs to be more than 8 character long, {hostname}')
        return cleaned_data

or cleaner:

class DeviceDetailForm(ModelForm):
    class Meta:
        model= DeviceDetail
        fields= ['hostname', 'mgt_interface', 'mgt_ip_addr', 'subnetmask', 'ssh_id', 'ssh_pwd', 'enable_secret', 'device_model']

    def clean_hostname(self):
        hostname = self.cleaned_data['hostname']
        if len(hostname) < 8:
            raise forms.ValidationError(f'Hostname needs to be more than 8 character long, {hostname}')
        return hostname

You can set the .device_model_id with the primary key of the altered device with:

if request.method == 'POST':
    device_frm = DeviceForm(request.POST)
    dd_frm = DeviceDetailForm(request.POST)
    if device_frm.is_valid() and dd_from.is_valid():
        device = device_frm.save()
        if dd_frm.is_valid():
            dd_frm.instance.device_model = device
            dd_frm.save()
    # …

Note: In case of a successful POST request, you should make a redirect [Django-doc] to implement the Post/Redirect/Get pattern [wiki]. This avoids that you make the same POST request when the user refreshes the browser.

Upvotes: 0

Related Questions