Search code examples
djangoformsauthenticationvalidation

Handling form fields in django for logged in user


Im trying to handle the existing name of a Category, so that users wont be allowed to create 2 categories with the same name, but at the moment its taking all categories from the database, not only for the logged-in user. I dont know how and where to implement request.user. I`m building an inventory app where everyone creates their own categories and adds items. Thank you.

This is my model:

class Category(models.Model):
    user = models.ForeignKey(User, default=1, on_delete=models.CASCADE,
                             related_name='category', null=True)
    name = models.CharField(max_length=100, null=False, blank=False)
    slug = models.SlugField(max_length=100)
    created_on = models.DateTimeField(auto_now_add=True)
    timestamp = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-timestamp']
        verbose_name = 'category'
        verbose_name_plural = 'categories'

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('category_detail', kwargs={'slug': self.slug})

This is my form:

class CategoryForm(forms.ModelForm):
    add_category = forms.BooleanField(widget=forms.HiddenInput, initial=True)

    class Meta:
        model = Category
        fields = ['name']

    def clean_name(self):
        name = self.cleaned_data.get('name')
        if (name == ""):
            raise forms.ValidationError('This field cannot be left blank')

        for instance in Category.objects.all():
            if instance.name == name:
                raise forms.ValidationError('There is a category with the name ' + name)
        return name

This is my view:

@login_required
def index(request):

    categories = Category.objects.filter(user=request.user)
    items = Item.objects.all()
    add_item = ItemForm()
    add_category = CategoryForm()
    query = None

    if request.method == 'POST':
        if 'add_item' in request.POST:
            add_item = ItemForm(request.POST)
            if add_item.is_valid():
                form = add_item.save(commit=False)
                form.category = get_object_or_404(
                    Category, name=request.POST.get('category'),
                    user=request.user)
                add_item.save()
                return redirect('home')
        else:
            add_category = CategoryForm(request.POST)
            if add_category.is_valid():
                category_form = add_category.save(commit=False)
                category_form.save()
                messages.success(request, f'{name} has been added')
                return redirect('home')

Edit Category View

@login_required
def category_edit(request, pk):
    category = Category.objects.get(id=pk)
    if request.method == 'POST':
        form = CategoryForm(request.POST, instance=category)
        if form.is_valid():
            form.save()
            messages.info(request, f'{category.name} has been updated!')
            return redirect('home')
    else:
        form = CategoryForm(instance=category, user=request.user)

    context = {
        'form': form,
    }
    return render(request, 'category_edit.html', context)

Ive tried adding user = request.user in the form class, but that resulted in an error Ive tried adding category_form.user = request.user before saving the form but that was still taking names from every other user


Solution

  • Pass the request's user to the form:

    class CategoryForm(forms.ModelForm):
        add_category = forms.BooleanField(widget=forms.HiddenInput, initial=True)
    
        def __init__(self, user, *args, **kwargs):
            self.user = user
            super().__init__(*args, **kwargs)
    
        class Meta:
            model = Category
            fields = ['name']
    
        def clean_name(self):
            name = self.cleaned_data.get('name')
            if (name == ""):
                raise forms.ValidationError('This field cannot be left blank')
            qs = Category.objects.filter(user=self.user, name=name)
            if self.instance.pk:
                # EXCLUDE CURRENT INSTANCE TO ENABLE EDIT
                qs = qs.exclude(pk=self.instance.pk)
            if qs.exists():
                raise forms.ValidationError('There is a category with the name ' + name)
            return name
    

    then in the view you need to pass the user:

    @login_required
    def index(request):
    
        categories = Category.objects.filter(user=request.user)
        items = Item.objects.all()
        add_item = ItemForm()
        add_category = CategoryForm(user=request.user)
        query = None
    
        if request.method == 'POST':
            if 'add_item' in request.POST:
                add_item = ItemForm(request.POST)
                if add_item.is_valid():
                    form = add_item.save(commit=False)
                    form.category = get_object_or_404(
                        Category, name=request.POST.get('category'),
                        user=request.user)
                    add_item.save()
                    return redirect('home')
            else:
                add_category = CategoryForm(user=request.user, data=request.POST)
                if add_category.is_valid():
                    category_form = add_category.save(commit=False)
                    category_form.save()
                    messages.success(request, f'{name} has been added')
                    return redirect('home')