Как запросить поле ForeignKey, указывающее на другое поле ForeignKey в Django

У меня есть модель (Letter) с внешним ключом, указывающая на другую модель (Company) с внешним ключом. Ниже приведена простая схема из models.py

from django.contrib.auth.models import User

class Company (models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, editable=False)
    date_created = models.DateField(default=timezone.now, null=True)
    company_name = models.CharField(max_length=100, null=True)

class Letter(models.Model):
    company = models.ForeignKey(Company, related_name = "company_letter", on_delete=models.CASCADE, null=True)
    subject = models.CharField(max_length=5000, null=True)
    body = models.TextField()

Я создал форму, в которой пользователи могут создавать Письма с моделью через ModelForm. В настоящее время, когда пользователь получает форму для создания письма, в поле Company появляются все компании всех пользователей. См. рисунок ниже:

Передняя конечная форма

Я хотел бы, чтобы в выпадающем списке отображались только те компании, которые создал пользователь, а не все компании от всех пользователей. Или чтобы можно было выбрать первую компанию, которую создал пользователь.

В форме можно указать вошедшего пользователя и соответствующим образом отфильтровать. В конструкторе формы мы таким образом ограничиваем набор запросов с:

class LetterForm(forms.ModelForm):
    
    def __init__(self, *args, user=None, **kwargs): 
        super().__init__(*args, **kwargs)
        self.fields['company'].widget.attrs = {'class': 'input',}
        self.fields['subject'].widget.attrs = {'class': 'input', 'placeholder': 'RE: …'}
        self.fields['body'].widget.attrs = {'class': 'textarea',}
        if user is not None:
            self.fields['company'].queryset = Company.objects.filter(user=user)
    
    # …

и в представлении мы передаем вошедшего пользователя в конструктор формы:

from django.contrib.auth.decorators import login_required

@login_required
def letter_form(request):
    if request.method == 'POST':
        form = LetterForm(request.POST, request.FILES, user=request.user)
        form.instance.user = request.user
        if form.is_valid():
            form.save() 
            return redirect('letter') 
    else:
        form = LetterForm(user=request.user)
    # …

Note: You can limit views to a view to authenticated users with the @login_required decorator [Django-doc].


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.

Если вы используете ModelForm для Letter, то это может выглядеть следующим образом:

class LetterForm(forms.ModelForm):
    class Meta:
        model = Letter
        fields = ["company", "subject", "body"]
    
    # you need to init the form with the right user instance
    def __init__(self, user=None, *args, **kwargs):
        # call the default __init__ behavior
        super().__init__(*args, **kwargs)
        # this is the trick, you will filter the companies queryset here
        if user:
             self.fields['company'].queryset = Company.objects.filter(user=user)

Так что вам нужно передать пользователя в форму из вашего представления:

что-то вроде:

def my_view(request):
    # assuming the user follow the standard Django user implementation
    # and you user is logged in
 
    form = LetterForm(request.POST or None, user=request.user)
    if form.is_valid():
        form.save()
        # then redirect the user to whatever success page 
        
    # render the form
    return render(request, "your_template.html", {"form": form})

Вернуться на верх