django-formset custom queryset

I am using the https://django-formset.fly.dev/ library, version 1.7.6. I’m trying to set a custom queryset on a ModelChoiceField, but I’m having trouble filtering the queryset based on the request object. Specifically, I want to filter and set the queryset to include only certain InRGPItem objects.

Could you advise on how to properly set a custom queryset for ModelChoiceField in this context?

forms.py

# django libraries
from django.forms.widgets import HiddenInput
from django.forms.models import ModelChoiceField, ModelForm

# django-formset libraries
from formset.collection import FormCollection
from formset.widgets import Selectize, DateInput, TextInput, Button
from formset.renderers.bootstrap import FormRenderer as BootstrapFormRenderer

# project models
from rgp_entry_app.models import OutRGPEntry, OutRGPItem, InRGPItem


class OutRGPItemForm(ModelForm):

    in_rgp_item = ModelChoiceField(
        label="In RGP Item",
        queryset=InRGPItem.objects.none(),  # Using direct empty queryset instead
        empty_label="Select",
        # to_field_name="guid",
        # widget=Selectize(
        #     search_lookup="name__icontains",
        # ),
    )
    class Meta:
        model = OutRGPItem
        fields = ['in_rgp_item', 'sent_qty', 'note']
        widgets = {
            'note': Textarea(attrs={'rows': 1}),
        }

class OutRGPItemCollection(FormCollection):
    outrgpitem = OutRGPItemForm()         # ✅ repeatable formset items
    related_field = 'out_rgp_entry'
    legend = "Out RGP Items"
    min_siblings = 1
    is_sortable = True
    ignore_marked_for_removal = True

class OutRGPEntryCollection(FormCollection):
    outrgpentry = OutRGPEntryForm()
    outrgpitem_set = OutRGPItemCollection()
    legend = "Out RGP Entry"
    default_renderer = BootstrapFormRenderer(
        form_css_classes='row',
        field_css_classes={
            # '*': 'mb-2 col-4',
            'chalan_no': 'col-sm-4',
            'chalan_date': 'col-sm-4',
            'rgp_date': 'col-sm-4',
            'in_rgp_item': 'mb-2 col-sm-4',
            'sent_qty': 'mb-2 col-sm-4',
            'note': 'mb-2 col-sm-4',
        },
    )

views.py

# django libraries import
from django.shortcuts import render, redirect, get_object_or_404
from django.http import HttpRequest, HttpResponseRedirect, JsonResponse, HttpResponse, HttpResponseBadRequest

# third-party libraries
from formset.views import FormCollectionView, FormView, EditCollectionView, FormViewMixin

# project forms import
from rgp_entry_app.forms import OutRGPEntryCollection

# project models import
from rgp_entry_app.models import RGPCustomer, InRGPEntry, InRGPItem


class OutRGPEntryCreateView(EditCollectionView):
    pk_url_kwarg = 'in_rgp_entry_guid'
    model = InRGPEntry
    collection_class = OutRGPEntryCollection
    template_name = 'admin_panel/rgp_entry/out_rgp_entry/out_entry_form.html'
    success_url = reverse_lazy("rgp_entry_app:rgp_entry_view")

    def get_object(self, queryset=None):
        if queryset is None:
            queryset = self.get_queryset()
        guid = self.kwargs.get(self.pk_url_kwarg)
        if guid is not None:
            queryset = queryset.filter(guid=guid)
        obj = get_object_or_404(queryset)
        return obj

    def get_initial(self):
        print(f"START:get_initial")
        initial = super().get_initial()
        print(f"BEFORE:initial: {initial}")
        in_rgp_entry = self.get_object()
        print(f"in_rgp_entry: {in_rgp_entry}")
        
        # Set initial values for the main form
        initial['outrgpentry'] = {
            'in_rgp_entry': in_rgp_entry.id,
        }
        
        # Set initial values for the items collection
        initial['outrgpitem_set'] = []
        for item in in_rgp_entry.inrgpitem_set.all():
            initial['outrgpitem_set'].append({
                'outrgpitem' : {
                    'in_rgp_item': item.id,  # Use item.id instead of item object
                    'sent_qty': item.qty,  # Set initial sent_qty to the original qty
                }
            })
            
        print(f"AFTER:initial: {initial}")
        print(f"END:get_initial")
        return initial

    def get_context_data(self, **kwargs):
        print(f"START:get_context_data")
        context = super().get_context_data(**kwargs)
        context["rgp_entry_nav"] = "active"
        context["in_rgp_entry"] = self.get_object()  # Add InRGPEntry object to context
        print(f"END:get_context_data")
        return context

models.py

# django libraires
from django.db import models
from django.utils import timezone

# project model
from auth_app.models import CreatedAtAndUpdatedAt

# third party libraries
import uuid

# Create your models here.
class ActiveCustomerManager(models.Manager):
    def get_queryset(self):
        """Retrieve all active users"""
        return super().get_queryset().filter(is_active=True)

class InActiveCustomerManager(models.Manager):        
    def get_queryset(self):
        """Retrieve all inactive users"""
        return super().get_queryset().filter(is_active=False)

class RGPCustomer(CreatedAtAndUpdatedAt):
    guid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    name = models.CharField(max_length=255, unique=True)
    created_by = models.ForeignKey('auth_app.User', on_delete=models.PROTECT, null=True, blank=False, related_name="rgp_customer_created_by")
    updated_by = models.ForeignKey('auth_app.User', on_delete=models.PROTECT, null=True, blank=False, related_name="rgp_customer_updated_by")
    is_active = models.BooleanField("active", default=True)
    deleted_at = models.DateTimeField(null=True, blank=True)  # Store delete timestamp

    objects = models.Manager()
    active_customers = ActiveCustomerManager()
    in_active_customers = InActiveCustomerManager()

    class Meta:
        ordering = ['-id']

    def __str__(self):
        return self.name
    
    def delete(self, *args, **kwargs):
        """Soft delete method using is_active field."""
        self.is_active = False
        self.deleted_at = timezone.now()
        self.save(update_fields=["is_active", "deleted_at"])

    def restore(self):
        """Restore a soft-deleted user."""
        self.is_active = True
        self.deleted_at = None
        self.save(update_fields=["is_active", "deleted_at"])


class ActiveRGPItemManager(models.Manager):
    def get_queryset(self):
        """Retrieve all active users"""
        return super().get_queryset().filter(is_active=True)

class InActiveRGPItemManager(models.Manager):        
    def get_queryset(self):
        """Retrieve all inactive users"""
        return super().get_queryset().filter(is_active=False)

class RGPItem(CreatedAtAndUpdatedAt):
    guid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    name = models.CharField(max_length=255, unique=True)
    is_active = models.BooleanField("active", default=True,)
    created_by = models.ForeignKey('auth_app.User', on_delete=models.PROTECT, null=True, blank=False, related_name="rgp_item_created_by")
    updated_by = models.ForeignKey('auth_app.User', on_delete=models.PROTECT, null=True, blank=False, related_name="rgp_item_updated_by")
    deleted_at = models.DateTimeField(null=True, blank=True)  # Store delete timestamp

    objects = models.Manager()
    active_rgp_items = ActiveRGPItemManager()
    in_active_rgp_items = InActiveRGPItemManager()

    def __str__(self):
        return self.name
    
    def delete(self, *args, **kwargs):
        """Soft delete method using is_active field."""
        self.is_active = False
        self.deleted_at = timezone.now()
        self.save(update_fields=["is_active", "deleted_at"])

    def restore(self):
        """Restore a soft-deleted user."""
        self.is_active = True
        self.deleted_at = None
        self.save(update_fields=["is_active", "deleted_at"])

class InRGPEntry(CreatedAtAndUpdatedAt):
    guid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    rgp_customer = models.ForeignKey(RGPCustomer, on_delete=models.CASCADE)
    rgp_no = models.CharField(max_length=50, unique=True)
    rgp_date = models.DateField()
    created_by = models.ForeignKey('auth_app.User', on_delete=models.PROTECT, null=True, blank=False, related_name="in_rgp_entry_created_by")
    updated_by = models.ForeignKey('auth_app.User', on_delete=models.PROTECT, null=True, blank=False, related_name="in_rgp_entry_updated_by")

    def __str__(self):
        return f"{self.rgp_no} - {self.rgp_customer.name}"


class InRGPItem(CreatedAtAndUpdatedAt):
    guid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    in_rgp_entry = models.ForeignKey(InRGPEntry, on_delete=models.CASCADE)
    rgp_item = models.ForeignKey(RGPItem, on_delete=models.CASCADE)
    serial_no = models.PositiveSmallIntegerField()
    qty = models.PositiveSmallIntegerField()

    def __str__(self):
        return f"{self.serial_no} - {self.rgp_item.name} (Qty: {self.qty})"


class OutRGPEntry(CreatedAtAndUpdatedAt):
    guid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    in_rgp_entry = models.ForeignKey(InRGPEntry, on_delete=models.CASCADE)
    chalan_no = models.CharField(max_length=50, unique=True)
    chalan_date = models.DateField()
    created_by = models.ForeignKey('auth_app.User', on_delete=models.PROTECT, null=True, blank=False, related_name="out_rgp_entry_created_by")
    updated_by = models.ForeignKey('auth_app.User', on_delete=models.PROTECT, null=True, blank=False, related_name="out_rgp_entry_updated_by")

    def __str__(self):
        return f"{self.chalan_no} - {self.in_rgp_entry.rgp_no}"


class OutRGPItem(CreatedAtAndUpdatedAt):
    guid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    out_rgp_entry = models.ForeignKey(OutRGPEntry, on_delete=models.CASCADE)
    in_rgp_item = models.ForeignKey(InRGPItem, on_delete=models.CASCADE)
    sent_qty = models.PositiveSmallIntegerField()
    note = models.TextField(blank=True, null=True)

    def __str__(self):
        return f"{self.in_rgp_item.serial_no} - {self.in_rgp_item.rgp_item.name} (Sent: {self.sent_qty})"

django-formset is a separate library and you should ask questions on their discussion board.

2 Likes

Don’t try to set queryset inside forms.py. Create the for without queryset.

Inside the view when form is used, set the queryset like below.

  1. Type
from django import forms
from .models import MyModel

class MyForm(forms.Form):
       
       
from django.shortcuts import render
from .forms import MyForm

def my_view(request):

    form = MyForm(self.fields['my_field'].queryset = MyModel.objects.filter(user=user))
# or
    form = MyForm(my_field.queryset = MyModel.objects.filter(user=user))



    return render(request, 'my_template.html', {'form': form})
  1. Type
from django import forms
from .models import MyModel

class MyForm(forms.Form):
    my_field = forms.ModelChoiceField(queryset=MyModel.objects.none())

    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user', None)
        super().__init__(*args, **kwargs)
        if user:
            self.fields['my_field'].queryset = MyModel.objects.filter(user=user)
from django.shortcuts import render
from .forms import MyForm

def my_view(request):

    form = MyForm(user=request.user)
    return render(request, 'my_template.html', {'form': form})
1 Like