Initializing a modelchoicefield to a value in get_context_data()

Viewed 16

I need to initialized a ModelChoiceField value to an instance retrieved in get_context_data() but it does not seem to work. I have tried .initial= but no luck so far.

This is the form

class PostEditForm(forms.ModelForm):
    post_category = forms.ModelChoiceField(label='Category',queryset=Category.objects.all(),empty_label="--Select Category--",required=False)
    class Meta:
        model=Post
        fields=('title','summary','content')

and this is the view

class EditPostView(LoginRequiredMixin, AjaxableResponseMixin, UpdateView):
    model = Post
    form_class = PostEditForm
    template_name = 'postapp/editpost.html'
    success_url = reverse_lazy('postapp:draftposts')

    def get_context_data(self):
        context = super().get_context_data()
        post=self.get_object()
        print(post)
        try:
            post_category = PostCategory.objects.get(post=post)
            print(post_category)
        except:
            print("Post Category not yet assigned")
        else:
            context['form']['post_category'].initial = post_category
            print(context['form']['post_category'])
        return context

and here is the post category model

class PostCategory(models.Model):
    post = models.ForeignKey(Post,on_delete=models.CASCADE)
    category = models.ForeignKey(Category,on_delete=models.CASCADE)
    def __str__(self):
        return self.category.title

UPDATE I forgot to mention I was rendering the form manually, here is the HTML

<select class="form-select" name={{form.post_category.html_name}} id='{{form.post_category.auto_id}}' 
  {% if form.post_category.field.required %}required{% endif %}>
    {% for value, text in form.post_category.field.choices  %}
      <option 
        {% if form.post_category.value == value|stringformat:"s" %}selected="selected"{% endif %} value={{value}}>{{text}}
      </option>
    {% endfor %}    
</select>
1 Answers

The value in the initial needs to be PK. So:

context['form']['post_category'].initial = post_category.id

Also, since post and post category are mapped 1-to-many, PostCategory.objects.get() can return multiple post categories which raises MultipleObjectsReturned error. I suggest using a combination of filter and first so you get the first category if there are many. You will probably change this logic to something that makes sense but it's important to be aware of it.

post_category = PostCategory.objects.filter(post=post).first()

Bonus points: override the get_initial method as it's better practice when using class based views.

def get_initial(self):
    initial = super().get_initial()

    post=self.object
    print(post)
    post_category = PostCategory.objects.filter(post=post).first()
    
    if post_category is not None:
        print(post_category)
        initial.update({'post_category': post_category.id})
        print(initial['post_category'])
    else:
        print("Post Category not yet assigned")

    return initial
Related