Assign currently LoggedIn user to ForeignKey field in Django

50 views Asked by At

My problem is I cannot auto assign my logged in user in seller instance of my model. I have tried many solutions from stackoverflow but none of them worked. I dont know what the problem is. I have tried both class based view and function based view. Here is class based approach.

My model:

from django.db import models
from django.contrib.auth.models import User
from PIL import Image

class Product(models.Model):
    title = models.CharField(max_length= 100)
    price = models.DecimalField(decimal_places=2, max_digits=9, default=0.00)
    description = models.TextField()
    discount_price = models.DecimalField(decimal_places=2, max_digits=8, default=0.00)
    image = models.ImageField(upload_to='product_image/', default="default.jpg", blank=True,  
    null=True)
    seller = models.ForeignKey(User, on_delete=models.CASCADE)

    def __str__(self):
        return self.title
    
    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)
        image = Image.open(self.image.path)
        if (image.height > 300 and image.width > 300):
            output_size = (300, 300)
            image.thumbnail(output_size)
            image.save(self.image.path)

My view:

from django.shortcuts import render, redirect
from .models import Product
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView
from django.urls import reverse_lazy

class CreateProduct(LoginRequiredMixin, CreateView):
    model = Product
    fields = '__all__'
    template_name = 'product/product_create.html'
    success_url = reverse_lazy('home')

    def form_valid(self, form):        
        form.instance.seller = self.request.user
        return super().form_valid(form) 

My Urls:

from django.urls import path
from .views import  CreateProduct, detail_product, delete_product, update_product

app_name = 'product'

urlpatterns = [
    path('create', CreateProduct.as_view(), name='create'),
]

My template:

{% extends 'base.html' %}

{% load crispy_forms_tags%}

{% block main %}

<div class="container mt-3">
    <form action="{% url 'product:create' %}" method="post" enctype="multipart/form-data">
        {% csrf_token %}

        {{form|crispy}}

        <button type="submit" class="btn btn-warning">ADD</button>
    </form>
</div>

{% endblock %}
1

There are 1 answers

1
willeM_ Van Onsem On BEST ANSWER

You should not specify fields = '__all__', since then the form will include a form element for the seller in the form, and overwrite the request.user. This will thus "undo" the logic of the form.instance.seller = request.user.

You can exclude the field with:

class CreateProduct(LoginRequiredMixin, CreateView):
    model = Product
    # no seller
    fields = ['title', 'price', 'description', 'discount_price', 'image']
    template_name = 'product/product_create.html'
    success_url = reverse_lazy('home')

    def form_valid(self, form):        
        form.instance.seller = self.request.user
        return super().form_valid(form)

An alternative, and perhaps more elegant, is to mark the field as non-editable:

class Product(models.Model):
    # …
    seller = models.ForeignKey(User, editable=False, on_delete=models.CASCADE)

Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.


Note: In Django, class-based views (CBV) often have a …View suffix, to avoid a clash with the model names. Therefore you might consider renaming the view class to CreateProductView, instead of CreateProduct.