Search code examples
djangodjango-modelsdjango-formsdjango-viewsdjango-generic-views

Need help getting correct instance for form_valid in a generic view


I can't work out how to get the correct instance for the form_valid part of my generic view. I am trying to allow a user to post on their project wall(bit like Facebook). I need the post to be related to an individual project(a user can have more than one project). Should the instance be a pk or the project title? Any example code or help would be very appreciated! I struggle understanding how when you create a new post, it knows which project to associate itself with.

views

class NewPost(CreateView):
    model = ProjectPost
    form_class = ProjectPostForm
    template_name = 'howdidu/new_post.html'

    def form_valid(self, form):
        newpost = form.save(commit=False)
        form.instance.user = self.request.user
        newpost.save()
        self.object = newpost
        return super(NewPost, self).form_valid(form)

    def get_success_url(self):
        project_username = self.request.user.username
        project_slug = self.object.slug
        return reverse('user_project', kwargs={'username':project_username, 'slug': project_slug})

models

class UserProject(models.Model):
    user = models.ForeignKey(User)
    title = models.CharField(max_length=100)
    project_overview = models.CharField(max_length=1000)
    project_picture = models.ImageField(upload_to='project_images', blank=True)
    date_created = models.DateTimeField(auto_now_add=True)
    project_views = models.IntegerField(default=0)
    project_likes = models.IntegerField(default=0)
    project_followers = models.IntegerField(default=0)
    slug = models.SlugField(max_length=100, unique=True) #should this be unique or not?

    def save(self, *args, **kwargs):
        self.slug = slugify(self.title)
        super(UserProject, self).save(*args, **kwargs)

    def __unicode__(self):
        return self.title

class ProjectPost(models.Model):
    project = models.ForeignKey(UserProject)
    title = models.CharField(max_length=100)
    post_overview = models.CharField(max_length=1000)
    date_created = models.DateTimeField(auto_now_add=True)
    post_views = models.IntegerField(default=0)
    post_likes = models.IntegerField(default=0)

forms

#form to add project details
class UserProjectForm(forms.ModelForm):

    class Meta:
        model = UserProject
        fields = ('title', 'project_picture', 'project_overview')

#form to create a post
class ProjectPostForm(forms.ModelForm):

    class Meta:
        model = ProjectPost
        fields = ('title', 'post_overview')

Solution

  • Ok, in that case, I would recommend a URL something like

    url(r'^(?P<pk>\d+)/post/add/$', views.NewPostCreateView.as_view(), name='...'),
    

    and then a view like

    class NewPost(CreateView):
       model = ProjectPost
       form_class = ProjectPostForm
       template_name = 'howdidu/new_post.html'
    
       def form_valid(self, form):
          self.object = form.save(commit=False)
    
          # Find project by using the 'pk' in the URL
          project = get_object_or_404(UserProject, pk=self.kwargs['pk'])
    
          # Then just set the project on the newPost and save()
          self.object.project = project
          self.object.save()
    
          return super(NewPost, self).form_valid(form)
    
       def get_success_url(self):
          # Unchanged ...
    

    I see in your code that you were trying to do something with the user but I don't understand why your Post does not have a user field (you may want to add a created_by) and the UserProject should already have a user set.

    I am also assuming the user got to the his/her project first, so you know by definition that the project he is adding a post to is his. If that is not the case, then just change the logic to get the UserProject through a regular query. e.g. maybe with `UserProject.objects.get(user = self.request.user) if there is one project per user (again, just as an example).

    Anyway, I am making some assumptions here, but hopefully the main question was how to set the project on the newPost and that is answered in my example.