Django: Using get_form_kwarg to pass url pramater into form __init__ for ModelChoiceField selection filiter
<p>I am building a FAQ app.</p>

<p>Model flow Topic -> Section -> Article.</p>

<p>Article has a FK to Section which has a FK to Topic.</p>

<p>In my create article from I want to take in the Topic_Pk so when the user selects a Section the choice selection is limited to just the Sections attached under the Topic.</p>

<p>I am using get_from_kwarg to pass the Topic_Pk from the url to <code>__init__</code> in the form. I keep getting a <code>TypeError __init__() got an unexpected keyword argument 'topic_pk'</code>. I do not want to pop the data or set topic_pk=None in the <code>__init__</code> parameters as this would invalidate the whole point.</p>

<p>What is it I am missing to allow me to use this variable?</p>

<p>Url:</p>

<pre><code>url(r'^ironfaq/(?P&lt;topic_pk&gt;\d+)/article/create$', ArticleCreateView.as_view()),
</code></pre>

<p>View:</p>

<pre><code>class ArticleCreateView(CreateView):
    model = Article
    form_class = CreateArticleForm
    template_name = "faq/form_create.html"
    success_url = "/ironfaq"

    def get_form_kwargs(self):
        kwargs = super(ArticleCreateView,self).get_form_kwargs()
        kwargs.update(self.kwargs)
        return kwargs
</code></pre>

<p>Form:</p>

<pre><code>class CreateArticleForm(forms.ModelForm):

    section = forms.ModelChoiceField(queryset=Section.objects.none())

    def __init__(self, *args, **kwargs):
        super(CreateArticleForm, self).__init__(*args, **kwargs)
        self.fields['section'].queryset = Section.objects.filter(topic_pk=self.kwargs['topic_pk'])

    class Meta:
        model = Article
        widgets = {
            'answer': forms.Textarea(attrs={'data-provide': 'markdown', 'data-iconlibrary': 'fa'}),
        }
        fields = ('title','section','answer')
</code></pre>

<p>Model:</p>

<pre><code>class Article(Audit):
    title = models.CharField(max_length=255)
    sort = models.SmallIntegerField()
    slug = models.SlugField()
    section = models.ForeignKey(Section,on_delete=models.CASCADE)
    answer = models.TextField()
    vote_up = models.IntegerField()
    vote_down = models.IntegerField()
    view_count = models.IntegerField(default=0)

    class Meta:
        verbose_name_plural = "articles"

    def __str__(self):
        return self.title

    def total_votes(self):
        return self.vote_up + self.vote_down

    def percent_yes(self):
        return (float(self.vote_up) / self.total_votes()) * 100

    def get_absolute_url(self):
        return ('faq-article-detail',(), {'topic__slug': self.section.topic.slug,
            'section__slug': self.section.slug, 'slug': self.slug})
</code></pre>

<p>For your current <code>__init__</code> signature, you <strong>must</strong> pop <code>topic_pk</code> from kwargs before you call <code>super()</code>, otherwise you'll get the <code>TypeError</code>. </p>

<p>In your question, you say that popping the value would 'invalidate the whole point', but I think you're mistaken. You can still use the <code>topic_pk</code> value after calling <code>super()</code>.</p>

<pre><code>class CreateArticleForm(forms.ModelForm):

    section = forms.ModelChoiceField(queryset=Section.objects.none())

    def __init__(self, *args, **kwargs):
        topic_pk = kwargs.pop('topic_pk')
        super(CreateArticleForm, self).__init__(*args, **kwargs)
        self.fields['section'].queryset = Section.objects.filter(topic_pk=topic_pk)
</code></pre>

<p>Another approach would be to use <code>topic_pk</code> as a named argument. Note that this changes the signature of the <code>__init__</code> method, so it might break other code (for example if you had <code>CreateArticleForm(request.POST)</code> somewhere else).</p>

<pre><code>    def __init__(self, topic_pk=None, *args, **kwargs):
        super(CreateArticleForm, self).__init__(*args, **kwargs)
        self.fields['section'].queryset = Section.objects.filter(topic_pk=topic_pk)
</code></pre>

python django