Passing parent object into CreateView for a child object

Ron :

I'm creating a dashboard to edit a tour app.

Per tour I have a child record in which I define steps. The 2 models look like this:

models.py

class Tour(models.Model):
    tour_id = models.CharField(primary_key=True,unique=True, max_length=10)
    country = models.ForeignKey(Countries, models.DO_NOTHING, db_column='country')
    language = models.ForeignKey(Language, models.DO_NOTHING, db_column='language')
    lastupddtm = models.DateTimeField(default=timezone.now)
    productid = models.CharField(max_length=50)
    title = models.CharField(max_length=50)
    description = models.CharField(max_length=100)
    descrlong = models.CharField(max_length=1000)
    live = models.CharField(max_length=1)
    image = models.ImageField(upload_to=upload_tour_image, storage=OverwriteStorage(), blank=True, null=True)

    class Meta:
        db_table = 'tour'
        verbose_name_plural = "tour"


    def get_language_flag(self):
        return self.language.flag.url

    def __str__(self):
        return str(self.tour_id) + ' - ' + str(self.title) + ' - ' + str(self.description)



class Toursteps(models.Model):
    # tour_id = models.OneToOneField(Tour, models.DO_NOTHING, db_column='tour_id')
    tour = models.ForeignKey(Tour, related_name='toursteps', on_delete=models.CASCADE)
    step = models.IntegerField(unique=True)
    title = models.CharField(max_length=50)
    description = models.CharField(max_length=100)
    descrlong = models.CharField(max_length=1000)
    audiotext = models.TextField()
    latitude = models.FloatField()
    longitude = models.FloatField()
    radius = models.FloatField()
    image = models.ImageField(upload_to=upload_tour_step_image, blank=True, null=True)

    class Meta:
        db_table = 'tourSteps'
        verbose_name_plural = "tourSteps"

    def __str__(self):
        return str(self.tour) + "|" + str(self.step)

After I created a Tour, I go to a detail page. From there I can click a link to add a step for this tour. This is where the problem is. I pass the tour_id as a variable into the url, but I can't find a way to pick it up in the CreateView of the step.

urls.py

urlpatterns = [
    path('tour/<str:pk>/detail', views.TourDetailView.as_view(), name='tour_detail'),
    path('tour/<str:pk>/edit', views.UpdateTourView.as_view(), name='tour_edit'),
    path('tour/<str:pk>/remove', views.DeleteTourView.as_view(), name='tour_remove'),
    path('tour/<str:tour_id>/step/new', views.CreateTourStepView.as_view(), name='tour_step_new')
]

Tour detail view

 <p><a href="{% url 'tour_step_new' tour_id=tour.pk %}"><span class="glyphicon glyphicon-plus"></span></a></p>

views.py

class CreateTourStepView(LoginRequiredMixin,CreateView):
    login_url = '/login/'
    redirect_field_name = 'tour_admin/tour_list.html'
    success_url = '/'
    form_class = TourStepForm
    model = Toursteps

    def get_context_data(self, **kwargs):
        context = super(CreateTourStepView, self).get_context_data(**kwargs)
        print(context['tour_id'])
        return context

forms.py

class TourStepForm(forms.ModelForm):

    class Meta():
        model = Toursteps
        #fields = '__all__'
        exclude = ('tour',)

    def form_valid(self, form):
        if form.is_valid():
            form.instance.tour_id = self.request.GET("tour_id")

            form.instance.save()

            return HttpResponseRedirect(self.get_success_url())

    def get_success_url(self):
        return reverse('tour_detail', kwargs={'pk':form.instance.tour_id})
dirkgroten :

First, your form_valid() and get_success_url() methods belong in your view, not in your form.

Second, the tour_id is passed to the view's kwargs, it's not a query parameter, hence not in self.request.GET. You can find it in self.kwargs.

Third, you need to actually fetch the Tour from your database, not just assign the tour_id. I could post to any tour_id if I wanted and there's no guarantee the tour_id belongs to an actual Tour object. Return a 404 if the tour doesn't exist. And if it exists, assign it to the tour step.

Finally, you should not assign to and save form.instance. You should get the instance using step = form.save(commit=False), then assign to step and save step.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=397956&siteId=1