from django.views.generic import CreateView, ListView, UpdateView, DeleteView from django.views.generic.detail import DetailView from django.views.generic.edit import FormView, ModelFormMixin from django.http import JsonResponse from django.core import serializers from django.forms import modelformset_factory from django.db.models import Count from django.views.generic.base import RedirectView from django.utils.decorators import method_decorator from django.contrib.auth.decorators import login_required from django.contrib import messages from django.shortcuts import get_object_or_404, render, redirect from django.urls import reverse, reverse_lazy from rest_framework import viewsets from rest_framework.response import Response from rest_framework.decorators import list_route from rest_framework import permissions from .serializers import NoteSerializer, NotebookSerializer, NoteTagSerializer from .models import Note, Notebook, LuxTag from .forms import NoteForm, NotebookForm from utils.views import AjaxableResponseMixin ################## # Base Views ################## @method_decorator(login_required, name='dispatch') class BaseListView(ListView): pass @method_decorator(login_required, name='dispatch') class BaseDetailView(DetailView): pass @method_decorator(login_required, name='dispatch') class LoggedInViewWithUser(FormView): def get_form_kwargs(self, **kwargs): kwargs = super().get_form_kwargs(**kwargs) kwargs.update({'user': self.request.user}) return kwargs ################## # Note Views ################## class NoteListView(BaseListView): model = Note def get_queryset(self): if not self.request.user.is_anonymous: return Note.objects.prefetch_related('tags').filter(owner=self.request.user).select_related('notebook') def get_context_data(self, **kwargs): context = super(NoteListView, self).get_context_data(**kwargs) context['notebook_list'] = Notebook.objects.filter(owner=self.request.user).annotate(note_count=Count('note')) context['tag_list'] = LuxTag.objects.filter(note__owner=self.request.user).annotate(note_count=Count('note')) return context def get_template_names(self): # print("IP Address for debug-toolbar: " + self.request.META['REMOTE_ADDR']) if not self.request.user.is_anonymous: return ['notes/notes_list.html'] else: return ['sell.html'] class NoteDetailView(LoggedInViewWithUser, AjaxableResponseMixin, UpdateView): ''' POST only works as AJAX ''' model = Note form_class = NoteForm template_name = 'notes/notes_detail.html' def get_queryset(self): if not self.request.user.is_anonymous: return Note.objects.filter(owner=self.request.user) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['notebook_form'] = NotebookForm return context def form_valid(self, form): print(self.request.META) if "trash" in self.request.POST: form.instance.notebook = Notebook.include_trash.get(owner=self.request.user, name="Trash") self.object = form.save() messages.info(self.request, 'The note %s was moved to the trash. View trash' % (self.object.title), extra_tags='safe') return redirect('notes:list') else: self.object = form.save() tags = serializers.serialize("json", self.object.tags.all()) data = { 'tags': tags, 'notebook': {'name': self.object.notebook.name, 'color': self.object.notebook.color_rgb} } return JsonResponse(data, safe=True) class NoteCreateView(LoggedInViewWithUser, CreateView): model = Note form_class = NoteForm template_name = 'notes/notes_create.html' def form_valid(self, form): form.instance.owner = self.request.user self.object = form.save() return super(NoteCreateView, self).form_valid(form) def get_success_url(self): return reverse_lazy('notes:detail', kwargs={'slug': self.object.slug, 'pk': self.object.pk}) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['notebook_form'] = NotebookForm # context['notes_list'] = Note.objects.filter(owner=self.request.user).select_related() return context class NoteTagView(BaseListView): model = Note template_name = 'notes/notes_list.html' def get_queryset(self): ''' This can generate a crazy amount of joins if there's a lot of tags have to keep an eye on it. Would be better to do: from django.db.models import Q from functools import reduce from operator import and_, or_ #query = reduce(and_, (Q(tags__slug=t) for t in self.tag_list)) # Note.objects.filter(query, owner=self.request.user) But that doesn't work for some reason. ''' if not self.request.user.is_anonymous: try: tags = self.kwargs['slug'].split("+") except ValueError: tags = self.kwargs['slug'] self.tag_list = [x.strip() for x in tags] qs = Note.objects.prefetch_related('tags').filter(owner=self.request.user).select_related('notebook') for tag in self.tag_list: qs = qs.filter(tags__slug=tag) return qs def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) #context['notes_list'] = Note.objects.filter(owner=self.request.user) context['tags'] = LuxTag.objects.filter(slug__in=self.tag_list) context['tag_list'] = LuxTag.objects.filter(note__owner=self.request.user).annotate(note_count=Count('note')) context['notebook_list'] = Notebook.objects.filter(owner=self.request.user).annotate(note_count=Count('note')) return context ################## # Notebook Views ################## class NotebookListView(LoggedInViewWithUser, CreateView): model = Notebook form_class = NotebookForm template_name = 'notes/notebook_list.html' def get_queryset(self): if not self.request.user.is_anonymous: return Notebook.objects.filter(owner=self.request.user) def form_valid(self, form): form.instance.owner = self.request.user if "create" in self.request.POST: self.object = form.save() messages.info(self.request, 'The notebook %s has been created, you may edit it below.' % (self.object.name)) return redirect('notebooks:list') return super(NotebookListView, self).form_valid(form) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) forms = [] for n in Notebook.objects.filter(owner=self.request.user).prefetch_related('note_set').prefetch_related('note_set__tags').annotate(note_count=Count('note')): #notes = [] #tags = set(tag for tag in notes.tags_set.all().distinct() for notes in n.note_set.all()) #n.tag_list = tags n.tag_list = LuxTag.objects.filter(note__in=n.note_set.all()).distinct()[:3] forms.append(NotebookForm(instance=n)) context['notebook_form_list'] = forms #context['notebook_list'] = Notebook.objects.filter(owner=self.request.user).select_related().annotate(note_count=Count('note')) return context class NotebookUpdateView(LoggedInViewWithUser, UpdateView): model = Notebook form_class = NotebookForm template_name = 'notes/notebook_list.html' def get_queryset(self): if not self.request.user.is_anonymous: return Notebook.objects.filter(owner=self.request.user) def form_valid(self, form): form.instance.owner = self.request.user self.object = form.save() data = { 'notebook': {'name': self.object.name, 'color': self.object.color_rgb, 'permalink': self.object.slug} } return JsonResponse(data, safe=True) class NotebookDetailView(LoggedInViewWithUser, AjaxableResponseMixin, UpdateView): model = Notebook form_class = NotebookForm template_name = 'notes/notebook_detail.html' def get_queryset(self): if not self.request.user.is_anonymous: if self.kwargs["slug"] == 'trash': return Notebook.include_trash.filter(owner=self.request.user) else: return Notebook.objects.filter(owner=self.request.user) def get_object(self): notebook = get_object_or_404(self.get_queryset().prefetch_related('note_set').prefetch_related('note_set__tags'), owner=self.request.user, slug=self.kwargs["slug"]) self.form = NotebookForm(instance=notebook) return notebook def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) #context['notes_list'] = Note.objects.filter(owner=self.request.user).select_related() context['form'] = self.form context['tag_list'] = LuxTag.objects.filter(note__owner=self.request.user).annotate(note_count=Count('note')) context['notebook_list'] = Notebook.objects.filter(owner=self.request.user).annotate(note_count=Count('note')) return context ################## # API Views ################## class IsOwnerOrDeny(permissions.BasePermission): """ Custom permission to only allow owners to post to their endpoint """ def has_object_permission(self, request, view, obj): # Write permissions are only allowed to the owner of the snippet. return obj.owner == request.user class NoteViewSet(viewsets.ModelViewSet): """ API endpoint that allows notes to be viewed or edited. """ serializer_class = NoteSerializer permission_classes = (permissions.IsAuthenticated,) def get_queryset(self): return Note.objects.filter(owner=self.request.user).order_by('-date_created') def perform_create(self, serializer): serializer.save(owner=self.request.user, tags__owner=self.request.user) def get_object(self): obj = get_object_or_404(self.get_queryset(), pk=self.kwargs["pk"]) if obj.is_public: return obj else: self.check_object_permissions(self.request, obj) return obj class NotebookViewSet(viewsets.ModelViewSet): """ API endpoint that allows botebook to be viewed or edited. """ serializer_class = NotebookSerializer def get_queryset(self): return Notebook.objects.filter(owner=self.request.user).order_by('-date_created') def perform_create(self, serializer): serializer.save(owner=self.request.user) def get_menu_data(self, serializer): return Notebook.objects.filter(owner=self.request.user).order_by('-name')[:1] class TagViewSet(viewsets.ModelViewSet): """ API endpoint that allows notes to be viewed or edited. """ serializer_class = NoteTagSerializer permission_classes = (permissions.IsAuthenticated,) def get_queryset(self): return LuxTag.objects.filter(owner=self.request.user) def perform_create(self, serializer): serializer.save(owner=self.request.user)