简体   繁体   中英

Django 3 - CreateView with initial ForeignKey field

I am trying to create a "restaurant rating" app on Django 3. I have set up the following models:

# Table storing the different restaurants
class Restaurant(models.Model):
    restaurant_name = models.CharField(max_length=200, unique=True)
    restaurant_address = models.CharField(max_length=200)
    restaurant_street_number = models.CharField(max_length=10)
    restaurant_city = models.CharField(max_length=200)
    restaurant_cuisine_type = models.CharField(max_length=200)
    def __str__(self):
        return self.restaurant_name + ' - ' + self.restaurant_city


class UserReview(models.Model):
    # Defining the possible grades
    Grade_1 = 1
    Grade_2 = 2
    Grade_3 = 3
    Grade_4 = 4
    Grade_5 = 5
    # All those grades will sit under Review_Grade to appear in choices
    Review_Grade = (
        (1, '1'),
        (2, '2'),
        (3, '3'),
        (4, '4'),
        (5, '5')
    )
    restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
    user_review_grade = models.IntegerField(default=None, choices=Review_Grade) # default=None pour eviter d'avoir un bouton vide sur ma template
    user_review_comment = models.CharField(max_length=1500)
    def get_absolute_url(self):
        return reverse('restaurants:reviews', args=[self.id])

This is the form I am using:

# Form for user reviews per restaurant
class UserReviewForm(forms.ModelForm):
    class Meta:
        model = UserReview
        #  restaurant = forms.ModelChoiceField(queryset=Restaurant.objects.filter(pk=id))
        fields = [
            'restaurant',
            'user_review_grade',
            'user_review_comment'
        ]
        widgets = {
            'restaurant': forms.HiddenInput,
            'user_review_grade': forms.RadioSelect,
            'user_review_comment': forms.Textarea
        }
        labels = {
            'user_review_grade': 'Chose a satisfaction level:',
            'user_review_comment': 'And write your comments:'
        }

Here are my URLs:

app_name = 'restaurants'
urlpatterns = [
    # ex: /restaurants/
    path('', views.index, name='index'),

    # ex: /restaurants/15
    path('<int:restaurant_id>/', views.details, name='details'),

    # ex: /restaurants/test
    path('<int:restaurant_id>/test', views.Test.as_view(), name='test')

]

Template:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>TEST</title>
</head>
<body>
<h1>Write a review for this restaurant:</h1>
<form method="post">
    {% csrf_token %}
    <input type="Hidden" name="restaurant" value="{{restaurant.restaurant_id}}">
    {{ form.as_p }}
    <br>
    <input type="submit" value="Submit">
</form>
</body>
</html>

And finally my view:

# Test class to be renamed posts reviews as it should
class Test (CreateView):
    template_name = 'restaurants/TEST.html'
    form_class = UserReviewForm

    # Get the initial information needed for the form to function: restaurant field
    def get_initial(self, restaurant_id, *args, **kwargs):
        initial = super(Test, self).get_initial(**kwargs)
        initial['restaurant'] = 9
        return initial()

    # Post the data into the DB
    def post(self, request, *args, **kwargs):
        form = UserReviewForm(request.POST)
        if form.is_valid():
            review = form.save()
            print(review)  # Print so I cna see in cmd prompt that something posts as it should
            review.save()
        return render(request, 'restaurants/reviews.html', {'form': form})

I am facing an issue as when I define initial as an integrer (let's say 9 - as I put it to test if my form was actually posting) it posts with no issue the review in the DB for restaurant that holds id=9 However I can't get it to have the restaurant_id automatically populated depending on which restaurant's page we're visiting.

I tried the following as well as few other 'tricks' found here and there but nothing seems to do the cut...

# Test class to be renamed later IF it works...
class Test (CreateView):
    template_name = 'restaurants/TEST.html'
    form_class = UserReviewForm

    # Get the initial information needed for the form to function: restaurant field
    def get_initial(self, restaurant_id, *args, **kwargs):
        restaurant = get_object_or_404(Restaurant, pk=restaurant_id)
        initial = super(Test, self).get_initial(**kwargs)
        initial['restaurant'] = self.request.restaurant.pk  
        return initial()

    # Post the data into the DB
    def post(self, request, *args, **kwargs):
        form = UserReviewForm(request.POST)
        if form.is_valid():
            review = form.save()
            print(review)  # Print so I cna see in cmd prompt that something posts as it should
            review.save()
        return render(request, 'restaurants/reviews.html', {'form': form})

Any help would be appreciated to point me in the right direction :)

You can obtain the restaurant_id in the url parameters with self.kwargs['restaurant_id'] . So you can ue:

class Test(CreateView):

    def get_initial(self, *args, **kwargs):
        initial = super(Test, self).get_initial(**kwargs)
          
        return initial

    # …

That being said, it is rather odd to store this in a form anyway. You can simply set this when the form is valid. So we define the form without a restaurant:

class UserReviewForm(forms.ModelForm):
    class Meta:
        model = UserReview
        fields = [
            'user_review_grade',
            'user_review_comment'
        ]
        widgets = {
            'user_review_grade': forms.RadioSelect,
            'user_review_comment': forms.Textarea
        }
        labels = {
            'user_review_grade': 'Chose a satisfaction level:',
            'user_review_comment': 'And write your comments:'
        }

and in the CreateView , we then set the restaurant of that instance:

class Test(CreateView):
    template_name = 'restaurants/TEST.html'
    model = UserReview
    form_class = UserReviewForm
    success_url = …

    def form_valid(self, form):
        
        super().form_valid(form)

You here need to specify a success_url to which it will redirect in case the submission is succesful. In case a POST is successful, you need to make a redirect to implement the Post/Redirect/Get pattern [wiki] .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM