Search code examples
djangodjango-generic-viewsdjango-2.0

where to check for conditions in generic UpdateView in Django


I'm using Django 2.0

I have a model Note and using generic update view to update the note object.

The URL configuration is like

app_name = 'notes'
urlpatterns = [
    path('<int:pk>/', NoteUpdate.as_view(), name='update'),
]

which is accessbile via it's namespace setup in app.urls

/notes/<pk>

I want to make some condition check in the view before loading the view or saving updated value.

Since, a note can be shared with any user and there is single template to view and update the note. I want to check for whether the user is owner of the note or whether the note has been shared with the user and has write permission granted.

class NoteUpdate(UpdateView):
    template_name = 'notes/new_note.html'
    model = Note
    fields = ['title', 'content', 'tags']

    def get_context_data(self, **kwargs):
        context = super(NoteUpdate, self).get_context_data(**kwargs)

        """
        check if note is shared or is owned by user
        """
        note = Note.objects.get(pk=kwargs['pk'])
        if note and note.user is self.request.user:
            shared = False
        else:
            shared_note = Shared.objects.filter(user=self.request.user, note=note).first()

            if shared_note is not None:
                shared = True
            else:
                raise Http404

        context['note_shared'] = shared_note
        context['shared'] = shared

        return context

    @method_decorator(login_required)
    def dispatch(self, request, *args, **kwargs):
        return super(self.__class__, self).dispatch(request, *args, **kwargs)

This is what I tried in get_context_data() but it is giving KeyError at pk=kwargs['pk']

Also, get_context_data() is the best place to check for conditions or get_query()?


Solution

  • you needn't to get pk from kwargs because your note is already exists as self.object so you code will be

    class NoteUpdate(UpdateView):
        template_name = 'notes/new_note.html'
        model = Note
        fields = ['title', 'content', 'tags']
    
        def get_context_data(self, **kwargs):
            context = super(NoteUpdate, self).get_context_data(**kwargs)
    
            """
            check if note is shared or is owned by user
            """
            note = self.object
            if note and note.user is self.request.user:
                shared = False
            else:
                shared_note = Shared.objects.filter(user=self.request.user, note=note).first()
    
                if shared_note is not None:
                    shared = True
                else:
                    raise Http404
    
            context['note_shared'] = shared_note
            context['shared'] = shared
    
            return context
    
        @method_decorator(login_required)
        def dispatch(self, request, *args, **kwargs):
            return super(self.__class__, self).dispatch(request, *args, **kwargs)
    

    According to this good answer of where to use get_query_set and get_context_data:

    get_query_set()

    Used by ListViews - it determines the list of objects that you want to display. By default it will just give you all for the model you specify. By overriding this method you can extend or completely replace this logic. Django documentation on the subject.

    class FilteredAuthorView(ListView):
        template_name = 'authors.html'
        model = Author
    
        def get_queryset(self):
            # original qs
            qs = super().get_queryset() 
            # filter by a variable captured from url, for example
            return qs.filter(name__startswith=self.kwargs.name)
    

    get_context_data()

    This method is used to populate a dictionary to use as the template context. For example, ListViews will populate the result from get_queryset() as author_list in the above example. You will probably be overriding this method most often to add things to display in your templates.

    def get_context_data(self, **kwargs):
        data = super().get_context_data(**kwargs)
        data['page_title'] = 'Authors'
        return data
    

    And then in your template you can reference these variables.

    <h1>{{ page_title }}</h1>
    
    <ul>
    {% for author in author_list %}
        <li>{{ author.name }}</li>
    {% endfor %}
    </ul>