Search code examples
djangodjango-modelsdjango-formsforeign-keys

Foreign key usage?


To all the pros out there. I finally figure out the issue i am having and I have read the foreign key posts in the past but none answer the question i have. My knowledge of foreign key is to link 2 tables while 1 of the table has a pre stored input so the foreign key takes the pre stored input to link to the 2nd table. Now my question comes: Is there a way to use foreign key on new data? Such as using django form with 2 models. Lets say I have this page which is to add a totally new device (None of the 2 tables have any info on this new device). And when i key in the forms. I want to allow saving of the 1st model then pass on (lets say 1st model have the field of 'hostname' and 'ipaddr') 'hostname' to the 2nd table as foreign key so the rest of the fields( model 2) within the same form get saved. Is this possible?

I have my existing codes but it is not doing the above at all. It is currently registering the new device to a pre stored device at the moment and i dont know what how to change it as my knowledge of foreign key = need existing updates. I know how to do it if the page requires a login and sync the data to the log in user but this web doesnt require log in. Below are the codes:

views.py

def device_add(request):
if request.method == "POST":
    device_frm = DeviceForm(request.POST) ##Part A1
    dd_form = DeviceDetailForm(request.POST)
    if device_frm.is_valid():
        # Create and save the device
        # new_device here is the newly created Device object
        new_device = device_frm.save()

        if dd_form.is_valid():

            # Create an unsaved instance of device detail
            deviceD = dd_form.save(commit=False)

            # Set the device we just created above as this device detail's device
            deviceD.device = new_device

            # If you did not render the hostname for the device detail, set it from the value of new device
            deviceD.hostname = new_device.hostname
            deviceD.save()
        return render(request, 'interface/device_added.html',{'devices':device}) 

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

models.py

class Device(models.Model):
        hostname = models.CharField(max_length=50)
        ipaddr = models.CharField(max_length=15)
        date_added = models.DateTimeField(default=timezone.now)
    
        def __str__(self):
            return self.hostname
    
  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)
            dev_mod = models.CharField(max_length=50)
            device_model = models.ForeignKey(Device, on_delete=models.CASCADE)
            
            def __str__(self):
                return self.hostname

forms.py

class DeviceForm(ModelForm):
    class Meta:
        model= Device
        fields= ['hostname', 'ipaddr'] 



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

    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

More details in pic: So currently my form has Model: drop down box. This field is required to select a previous stored hostname to link this new device i registered. It is required as if i remove this, the form doesnt saved on the 2nd model: DeviceDetail. Currently this field appear due to the following codes in html: This part is removed

<div class="row">
                            

<div class="col-md-5">
                                <div class="form-group">
                                    <label for="{{dd_form.dev_mod_id.id_for_label}}">Model<span
                                            class="text-danger">*</span></label>
                                    {{dd_form.device_model}}

                                </div>
                            </div>
                        </div>

enter image description here

What I want to do is to remove this field and let it do its work at the back. Such as when i load this page(form) it doesnt have this field and when i key in all the details and press save. At the code, it takes the hostname i input and pass it on to the foreign key (Currently it takes the pre stored hostname as the foreign key) then saved this new hostname and the other details in the 2nd table.

enter image description here

Below is for the database: This is for device table once i add save new data enter image description here

This is device detail table enter image description here

Look at the value 11 beside the column with the input of Catalyst 9606R

This value 11 comes the id from the device table (Pre stored hostname)(This happens due to the module drop down box which appear in the form to ask me choose a previously stored hostname enter image description here

This is not i need. I need the new id which is 61 from the 3rd image


Solution

  • From what I understand, you want to create both Device and DeviceDetail in one POST.

    To support this, first remove device_model from the form and don't render it in your template (you don't have to, because we are putting a value for it later):

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

    Then in your views, save the device first. Then use that newly created device as foreign key of the device detail we are about to create:

    def device_add(request):
        if request.method == "POST":
            device_frm = DeviceForm(request.POST)
            dd_form = DeviceDetailForm(request.POST)
    
            if device_frm.is_valid():
    
                # Create and save the device
                # new_device here is the newly created Device object
                new_device = device_frm.save()
    
                if dd_form.is_valid():
    
                    # Create an unsaved instance of device detail
                    deviceD = dd_form.save(commit=False)
    
                    # Set the device we just created above as this device detail's device
                    deviceD.device_model = new_device
    
                    # If you did not render the hostname for the device detail, set it from the value of new device
                    deviceD.hostname = new_device.hostname
                    deviceD.save()