Kartal
Kartal

Reputation: 79

Django ForeignKey Model Form

I'm newbie on Django. I have two model and one of this model have Foreign Key. I'm using Model Form in forms and when I fill the form my foreign key field return null. What I want is when I fill the form foreign key field, fill according to the pointed out by the foreign key.

Models:

class customerInfo(models.Model):
customerName = models.CharField(max_length = 50)
customerContent = models.TextField(max_length = 50)
createdDate= models.DateTimeField(auto_now_add = True)

def __str__(self):
    return self.customerName

class productInfo(models.Model):
    username = models.CharField(max_length = 50)
    passwd = models.CharField(max_length = 50)
    destIp = models.CharField(max_length = 50)
    hostname = models.CharField(max_length = 50)
    productName = models.CharField(max_length = 50)
    customer = models.ForeignKey(customerInfo,on_delete = models.CASCADE,null=True)

    def __str__(self):
        return self.productName

Forms:

class customerForm(forms.ModelForm):
    class Meta:
        model = customerInfo
        fields = (
                "customerName",
        )

    class addProductForm(forms.ModelForm):
        class Meta:
            model = productInfo
            fields = (
                    "productName",
                    )


    class productInfoForm(forms.ModelForm):
            class Meta:
                    model = productInfo
                    fields = (
                            "username",
                            "passwd",
                            "destIp",
                            "hostname",
                    )

Views:

@login_required(login_url = "/")
def addCustomer(request):
    form = customerForm(request.POST or None)
    content = {"form" : form,}
    if form.is_valid():
        form.save()
        customerName = form.cleaned_data['customerName']
        return redirect("addproduct")

    else:
        return render(request,"addcustomer.html",content)

@login_required(login_url = "/")
def addProduct(request):
    form = addProductForm(request.POST or None)
    content = {"form" : form}
    if form.is_valid():
        global productName
        productName = form.cleaned_data['productName']
        return redirect("addproductinfo")
    return render(request,"addproduct.html",content)

@login_required(login_url = "/")
def addProductInfo(request):
    form = productInfoForm(request.POST or None)
    content = {"form" : form}
    if form.is_valid():
        p = form.save(commit = False)
        p.productName = productName
        p.save()
        return redirect("customer")
    return render(request,"addproductinfo.html",content)

As a result, I want to see the customer's products when I click on the customer name. Not all products. Before I can do that, the customer id fields needs to be full. I hope you understood me.

Upvotes: 2

Views: 1153

Answers (2)

openHBP
openHBP

Reputation: 691

Your question and code sample is not clear. First of all you should break down your model into several use cases:

  • Customer: list of customers, Create, Read, Update & Delete (CRUD) customer
  • Product: list of products, Create, Read, Update & Delete (CRUD) product

From the list of customers you can Read one and on the 'detail view displayed' you can Create, Update or Delete it.

From the list of products you can Read one and on the 'detail view displayed' you can Create, Update or Delete it.

Passing from the list of customer to the list of product can be done via an extra Button/Link displayed per line on your Customer List, so as your Button/Link used to display any Customer Detail.

The customer PrimaryKey (PK) is passed to the detail via the url definition.

path('customer/<pk>', views.customer_detail_view, name='customer_detail'),

This url is only for display. You're also need one for each DB operation: Create, Update, Delete. Find below urls.py code example for your customer. You'll need the same for the products.

from django.urls import path
from . import views

urlpatterns = urlpatterns + [
    path('customer', views.customer_list_view, name='customer_list'),
    path('customer/add', views.customer_add_view, name='customer_add'),
    path('customer/<pk>', views.customer_detail_view, name='customer_detail'),
    path('customer/<pk>/upd', views.customer_update_view, name='customer_update'),
    path('customer/<pk>/del', views.customer_delete_view, name='customer_delete'),
    ]

Note that create doesn't pass 'pk' since it is unknown yet...

The call to the Detail View from the List View is done in your html template

<tbody>
{% for i in customer_list %}
<tr>
  <td><a href="{% url 'customer_detail' pk=i.id %}">{{ i.customerName }}</a></td>
  <td>{{ i.customerContent|default_if_none:"" }}</td>
</tr>
{% endfor %}
</tbody>

The argument is passed by kwargs (dict) via the url and if you use ClassBasedView (generic.DetailView) it will be handled automatically. If not, you have to grab the kwargs like: kwargs.get('pk') or kwargs.pop('pk') the last one remove 'pk' from the kwargs. You could also pass the 'pk' using args (no pk key assignement) {% url 'customer_detail' i.id %}. This can also be defined directly in a get_absolute_url function of your model. def get_absolute_url(self): return reverse_lazy('customer_detail', args=[str(self.id)]) or def get_absolute_url(self): return reverse_lazy('customer_detail', kwargs={'pk': self.pk})

By doing that way you'll also be able to manage your 'productName' global variable, which should be avoided! By the way I don't understand why you're willing to separate the creation of productName and productInfo??? Why not keeping them all together?

Finally, if you want to display several possible encoding line for your Product, you should take a look at Django-FormSet. Search google for FormSet Tutorial but this is more an advanced feature.

A ProductFormset with 5 possible encoding lines would look like:

from django.forms import modelformset_factory

ProductFormset = modelformset_factory(
    productInfo,
    fields=('productName', ),
    extra=5,
    widgets={'name': forms.TextInput(attrs={
            'class': 'form-control',
            'placeholder': 'Enter product Name here'
        })
    }
)

Upvotes: 2

seba6m6
seba6m6

Reputation: 119

If you want to reuse the productInfo model then you shoud you models.ManyToManyField instead of the ForeignKey. As i understand correctly you want to have a product that multiple of the customers can "connect" to , right ?

for more --> https://docs.djangoproject.com/en/2.1/ref/models/fields/

and more --> https://www.revsys.com/tidbits/tips-using-djangos-manytomanyfield/

My usage:

class EventVocab(models.Model):
    word              = models.CharField(max_length = 30)
    word_chinese      = models.CharField(max_length = 30,blank=True, null=True)
    explanation       = models.TextField(max_length = 200)
    example           = models.TextField(max_length = 100)
    word_audio        = models.FileField(blank=True, null=True)
    explanation_audio = models.FileField(blank=True, null=True)
    example_audio     = models.FileField(blank=True, null=True)

class UserVocab(models.Model):
    event_vocab  = models.ManyToManyField(EventVocab, related_name='event_vocab')
    current_user = models.ForeignKey(User, related_name="vocab_owner", on_delete=models.CASCADE)

In this example UserVocab (in your case product) can be connected to just one User, but one user can have multiple event_vocabs (products)

Upvotes: 0

Related Questions