Ewanw
Ewanw

Reputation: 738

How to handle object reference in CreateView

I have an item. An item can have many reviews. I expect a review to be created within the context of an item. Therefore, I capture the pk from the url and add it to the context.

This is where I get stuck, I'm unsure how to access the context in form_valid and, more importantly, I'm concerned the path I'm trying to go down seems hacky.

Essentially when the user prepares to submit a review, the application will know what item it's in reference to. What's the most pythonic/django-onic way to do this?

Models

class Item(models.Model):
    name = models.CharField(max_length=100)
    source = models.ForeignKey('Source')   

class Review(models.Model):
    rating = models.CharField(max_length=30)
    value = models.CharField(max_length=30)
    date = models.DateField(auto_now_add=True)
    comment = models.CharField(blank=True,max_length=100) 

    item = models.ForeignKey(Item,blank=True)
    user = models.ForeignKey(User)

Urls

url(r'^review/create/item/(?P<itempk>\d+)',views.ReviewCreate.as_view(),name='review_create'),

Views

class ReviewCreate(CreateView):
    model = Review
    fields = ['rating', 'value', 'comment']

    def get_context_data(self, **kwargs):
        context = super(ReviewCreate, self).get_context_data(**kwargs)
        itempk = self.kwargs['itempk']
        item = get_object_or_404(Item, pk=itempk)
        context['item'] = item
        return context

    def form_valid(self, form):
        review = form.save(commit=False)
        review.user = self.request.user
        context = super(ReviewCreate, self).get_context_data(**kwargs) '''doesn't work'''
        review.item = context['item']

        return super(ReviewCreate, self).form_valid(form)


    template_name = 'food/review_form.html'

Upvotes: 1

Views: 62

Answers (1)

Alasdair
Alasdair

Reputation: 308849

The get_context_data method is meant to return the context for a template, so I agree that calling it in form_valid is a bit hacky.

You could fetch the item in the dispatch method instead and store it as self.item. Then you can retrieve the item in get_context_data and form_valid.

In form_valid you can modify form.instance - that way you don't have to save with commit=False.

class ReviewCreate(CreateView):
    model = Review
    fields = ['rating', 'value', 'comment']

    def dispatch(self, request, *args, **kwargs):
        itempk = self.kwargs['itempk']
        self.item = get_object_or_404(Item, pk=itempk)
        return super(ReviewCreate, self).dispatch(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super(ReviewCreate, self).get_context_data(**kwargs)
        context['item'] = self.item
        return context

    def form_valid(self, form):
        form.instance.user = self.request.user
        form.instance.item = self.item
        return super(ReviewCreate, self).form_valid(form)

    template_name = 'food/review_form.html'

Upvotes: 1

Related Questions