# -*- coding: utf-8 -*- import random from datetime import datetime from django.conf import settings from django.contrib.admin.views.decorators import staff_member_required from django.contrib.auth.decorators import login_required from django.core.context_processors import csrf from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist from django.core.paginator import ( EmptyPage, InvalidPage, PageNotAnInteger, Paginator ) from django.core.urlresolvers import reverse_lazy from django.db.models.deletion import ProtectedError from django.db.models.loading import get_model from django.db.models.query import QuerySet from django.http import Http404, HttpResponse, HttpResponseRedirect from django.shortcuts import render_to_response from django.utils.translation import get_language as lang from django.utils.translation import ugettext as _ from django.views.generic import ListView as OldListView from django.views.generic import DetailView, RedirectView from django.views.generic.detail import ( SingleObjectMixin, SingleObjectTemplateResponseMixin ) from django.views.generic.edit import ModelFormMixin, ProcessFormView from accounts.models import User from city.models import City from company.models import Company from conference.models import Conference from country.models import Country from exposition.models import Exposition from file.forms import FileModelForm from file.models import TmpFile from functions.forms import AdminSearchForm from functions.views_help import split_params from functions.http import JsonResponse from haystack.query import EmptySearchQuerySet, SearchQuerySet from haystack.backends import SearchNode from hvad.utils import get_translation_aware_manager from meta.models import MetaSetting from meta.views import Meta from photoreport.models import Photoreport from seminar.models import Seminar from theme.models import Tag, Theme from webinar.models import Webinar class ListView(OldListView): """ Default Django generic ListView with few overrided methods for redirecting onto first page of pagination in case of entering big page number(for search engines) List of modules, where overrided ListView is used: - accounts.views - article.views - company.views - conference.views - core.views - exposition.views - photologue.views - place_exposition.views - specialist_catalog.views - translator.views """ def paginate_queryset(self, queryset, page_size): """ Paginate the queryset, if needed. """ paginator = self.get_paginator(queryset, page_size, allow_empty_first_page=self.get_allow_empty()) page_kwarg = self.page_kwarg page = self.kwargs.get(page_kwarg) or self.request.GET.get(page_kwarg) or 1 try: page_number = int(page) except ValueError: if page == 'last': page_number = paginator.num_pages else: raise Http404(_("Page is not 'last', nor can it be converted to an int.")) try: page = paginator.page(page_number) self.kwargs['home'] = False except EmptyPage as e: page = paginator.page(1) self.kwargs['home'] = True return (paginator, page, page.object_list, page.has_other_pages()) def get(self, request, *args, **kwargs): response = super(ListView, self).get(request, *args, **kwargs) if self.kwargs.get("home"): path = self.request.path return HttpResponseRedirect(path[:path.find('page')]) else: return response @login_required def filtered_list(request, objects, template, item_per_page=settings.ADMIN_PAGINATION): """ Return template with objects in it with pagination item_per_page - how many objects view in the one page """ paginator = Paginator(objects, item_per_page) page = request.GET.get('page') try: objects = paginator.page(page) except PageNotAnInteger: # If page is not an integer, deliver first page. objects = paginator.page(1) except EmptyPage: # If page is out of range (e.g. 9999), deliver last page of results. objects = paginator.page(paginator._num_pages) return render_to_response(template, {'objects': objects, 'search_form': AdminSearchForm()}) @staff_member_required def objects_list(request, Model, template, item_per_page=settings.ADMIN_PAGINATION): """ Return template with all objects of model Model Model - objects Model item_per_page - how many objects view in the one page """ if request.GET: form = AdminSearchForm(request.POST) if form.is_valid(): s_name = request.GET.get('search_name') if s_name: objects = get_translation_aware_manager(Model).filter(name__contains=s_name).distinct() return filtered_list(request, objects, template, 1000) else: list = Model.objects.all() else: list = Model.objects.all() paginator = Paginator(list, item_per_page) page = request.GET.get('page') try: objects = paginator.page(page) except PageNotAnInteger: # If page is not an integer, deliver first page. objects = paginator.page(1) except EmptyPage: # If page is out of range (e.g. 9999), deliver last page of results. objects = paginator.page(paginator._num_pages) return render_to_response(template, {'objects': objects, 'search_form': AdminSearchForm()}) @login_required def add_object(request, Form, template_string, redirect_string, #required values choices={}, init_data ={}): if request.POST: form = Form(request.POST) #set choices filled by ajax if 'city' in choices: form.fields['city'].choices = [(item.id, item.name) for item in choices['city'].objects.filter(country=request.POST['country'])] if 'tag' in choices: form.fields['tag'].choices = [(item.id, item.name) for item in choices['tag'].objects.all()] if form.is_valid(): form.save() return HttpResponseRedirect(redirect_string) else: form = Form(initial=init_data) args = {} args.update(csrf(request)) args['languages'] = settings.LANGUAGES args['form'] = form return render_to_response(template_string, args) @login_required def add_object_with_file(request, Form, template, redirect_string, #required values choices={}, init_data ={}): """ Return form and FileForm and post it on the server. Create key which will be check tmp files If form is posted redirect on the page in redirect_string FileForm posts with ajax choices are ... """ #cheks if key already exist(when form wasn't validated) if request.POST.get('key'): key = request.POST['key'] else: key = random.getrandbits(128) #initial FileForm with key for checking if file connected to object file_form = FileModelForm(initial={'key': key}) if request.POST: form = Form(request.POST) #set choices filled by ajax if 'city' in choices: form.fields['city'].choices = [(item.id, item.name) for item in choices['city'].objects.filter(country=request.POST['country'])] if 'tag' in choices: form.fields['tag'].choices = [(item.id, item.name) for item in Tag.objects.all()] if form.is_valid(): form.save() return HttpResponseRedirect(redirect_string) else: init_data['key'] = key form = Form(initial=init_data) args = {} args.update(csrf(request)) #languages uses in template args['languages'] = settings.LANGUAGES args['form'] = form args['file_form'] = file_form #list of files connected to this form args['files'] = TmpFile.objects.filter(key=key) return render_to_response(template, args) @login_required def delete_object(request, Model, Form, url, prev_page,): """ Form must have 1 parameter url or id """ if request.GET: url = request.GET.get('url') id = request.GET.get('id') if url: object = Model.objects.get(url=url) else: object = Model.objects.get(id=id) try: object.delete() return HttpResponseRedirect(prev_page) except ProtectedError: msg = _(u'Удаления %(object)s требует удаления связаных объектов') % {'object': object} return render_to_response('delete.html', {'msg':msg, 'prev_page':prev_page}) else: try: object = Model.objects.get(url=url) except: object = Model.objects.get(id=url) form = Form(instance=object) args = {} args.update(csrf(request)) args['form'] = form args['object'] = object args['prev_page'] = prev_page return render_to_response('delete.html', args) class ExpoMixin(object): def get_params(self): model_names = {Exposition: _(u'Выставки'), Conference: _(u'Конференции'), Seminar: _(u'Семинары'), Webinar: _(u'Вебинары'), Company: _(u'Участники'), Photoreport: _(u'Фоторепортажи')} model_alternative_name = {Exposition: 'exposition', Conference: 'conference', Photoreport: 'photoreport'} params = [{'type':'model', 'url':self.model, 'name': model_names.get(self.model), 'alternative_name': model_alternative_name.get(self.model)}] st = self.kwargs.get('params') if st: params = params + split_params(st) return params single_page_filter = {Exposition:'event', Conference:'event', Seminar:'event', Webinar:'event', Company:'member', User:'visitor', Photoreport: 'photoreport'} class ExpoListView(ExpoMixin, ListView): """ """ paginate_by = settings.CLIENT_PAGINATION params = None single_page = False search_form = None order = None def get_queryset(self): query = self.model.objects.all() params = self.get_params() for param in params: if param.get('type') == 'country': country = Country.objects.safe_get(url=param.get('url')) if country: param['name'] = country.name query = query.filter(country=country) if param.get('type') == 'city': city = City.objects.safe_get(url=param.get('url')) if city: param['name'] = city.name query = query.filter(city=city) if param.get('type') == 'theme': theme = Theme.objects.safe_get(url=param.get('url')) if theme: param['name'] = theme.name query = query.filter(theme=theme) if param.get('type') == 'tag': tag = Tag.objects.safe_get(url=param.get('url')) if tag: param['name'] = tag.name query = query.filter(tag=tag) if param.get('type') == 'year': param['name'] = param.get('url') query = query.filter(data_begin__year=param.get('url')) if param.get('type') == 'month': monthes = {'jan': 1, 'feb': 2, 'mar': 3, 'apr': 4, 'may': 5, 'jun': 6, 'jul': 7, 'aug': 8, 'sep': 9, 'oct': 10, 'nov': 11, 'dec': 12} param['name'] = param.get('url') query = query.filter(data_begin__month=monthes.get(param.get('url'))) if param.get('type') == 'member' and self.model != Company: param['name'] = param.get('url') company = Company.objects.safe_get(url=param.get('url')) if company: param['name'] = company.name query = query.filter(company__in=[company]) if param.get('type') == single_page_filter.get(self.model): query = query.filter(url=param.get('url')) if query: self.single_page = True param['name'] = query[0].name #if self.request: # views = query[0].views # query.update(views=views+1) self.params = params if self.order: return query.order_by(self.order) else: return query def get_context_data(self, **kwargs): context = super(ExpoListView, self).get_context_data(**kwargs) context['filter'] = self.params context['single_page'] = self.single_page context['search_form'] = self.search_form if self.single_page: s = MetaSetting.objects.get(name='expo') obj = context['object_list'][0] params = s.generate_meta(obj) m = Meta(**params) context['meta'] = m return context class EventDetail(ExpoMixin, DetailView): def get_object(self, queryset=None): obj = Country.objects.filter()[0] return obj class ExpoSearchView(ListView): paginate_by = settings.CLIENT_PAGINATION template_name = None search_form = None model = None def get_queryset(self): if self.request.GET: form = self.search_form(self.request.GET) if form.is_valid(): return form.search() else: return EmptySearchQuerySet() else: return EmptySearchQuerySet() def get_context_data(self, **kwargs): context = super(ExpoSearchView, self).get_context_data(**kwargs) if self.request.GET: form = self.search_form(self.request.GET) if form.is_valid(): form.data_with_parents = form.get_form_data() if form.cleaned_data.get('th'): context['themes'] = form.cleaned_data.get('th') if form.cleaned_data.get('tg'): context['tag'] = form.cleaned_data.get('tg') if form.cleaned_data.get('co'): context['country'] = form.cleaned_data.get('co') if form.cleaned_data.get('ci'): context['city'] = form.cleaned_data.get('ci') if form.cleaned_data.get('fr'): context['date_from'] = form.cleaned_data.get('fr').strftime('%d.%m.%Y') if form.cleaned_data.get('to'): context['date_to'] = form.cleaned_data.get('to').strftime('%d.%m.%Y') if form.cleaned_data.get('q'): context['query'] = form.cleaned_data.get('q', '').strip() if form.cleaned_data.get('w'): context['where_query'] = form.cleaned_data.get('w', '').strip() else: form = self.search_form() context['search_form'] = form queries = self.request.GET.copy() if queries.has_key('page'): del queries['page'] context['queries'] = queries return context class SimpleObjectChangeView(RedirectView, SingleObjectMixin): model = None url = None slug_field = 'url' slug_url_kwarg = 'url' attr = None attr_state = None query_string = True def get_object(self, queryset=None): """ Returns the object the view is displaying. By default this requires `self.queryset` and a `pk` or `slug` argument in the URLconf, but subclasses can override this to return any object. """ # Use a custom queryset if provided; this is required for subclasses # like DateDetailView if queryset is None: queryset = self.get_queryset() qs = self.model._default_manager.none() # Next, try looking up by url or primary key. slug = self.kwargs.get(self.slug_url_kwarg, None) if slug is not None: slug_field = self.get_slug_field() qs = queryset.filter(**{slug_field: slug}) if not qs: qs = queryset.filter(pk=slug) # If none of those are defined, it's an error. else: raise AttributeError("Generic detail view %s must be called with " "either an object pk or a slug." % self.__class__.__name__) try: # Get the single item from the filtered queryset obj = qs.get() except ObjectDoesNotExist: raise Http404(_("No %(verbose_name)s found matching the query") % {'verbose_name': qs.model._meta.verbose_name}) return obj def change_object(self, **kwargs): setattr(self.object, self.attr, self.attr_state) self.object.save(update_fields=[self.attr]) def get(self, request, *args, **kwargs): self.object = self.get_object() self.change_object() print(self.request.META.get('QUERY_STRING', '')) return super(SimpleObjectChangeView, self).get(request, *args, **kwargs) class BlockedFilterMixin(object): '''Filters default queryset with excluding blocked items ''' def get_queryset(self): qs = super(BlockedFilterMixin, self).get_queryset() return qs.filter(blocked=False) class AjaxableResponseMixin(object): """ Mixin to add AJAX support to a form. Must be used with an object-based FormView (e.g. CreateView) """ def form_invalid(self, form): response = super(AjaxableResponseMixin, self).form_invalid(form) if self.request.is_ajax(): return JsonResponse(form.errors, status=400) else: return response def form_valid(self, form): # We make sure to call the parent's form_valid() method because # it might do some processing (in the case of CreateView, it will # call form.save() for example). response = super(AjaxableResponseMixin, self).form_valid(form) if self.request.is_ajax(): data = { 'pk': self.object.pk, 'success': True, } return JsonResponse(data) else: return response class CreateUpdateView(SingleObjectTemplateResponseMixin, ModelFormMixin, ProcessFormView): def get_object(self, queryset=None): try: return super(CreateUpdateView,self).get_object(queryset) except AttributeError: return None def get(self, request, *args, **kwargs): self.object = self.get_object() return super(CreateUpdateView, self).get(request, *args, **kwargs) def post(self, request, *args, **kwargs): self.object = self.get_object() return super(CreateUpdateView, self).post(request, *args, **kwargs) class ContextMixin(object): initial_ctx = {} def dispatch(self, request, *args, **kwargs): self.extra_ctx = self.initial_ctx.copy() return super(ContextMixin, self).dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): context = super(ContextMixin, self).get_context_data(**kwargs) context.update(self.extra_ctx) return context class ReverseOrderMixin(ContextMixin): def get_reversed_qs(self, qs): # exclude_fields ef = ['data_begin', 'data_end'] children = list(filter(lambda x: not set([y[0].col for y in x.children]).intersection(ef), qs.query.where.children)) qs.query.where.children = children self.extra_ctx['events_reversed'] = True return qs.filter(data_begin__lte=datetime.now()).order_by('-data_begin') def get_reversed_sqs(self, qs): # exclude_fields ef = ['data_begin'] childrens = [] for children in qs.query.query_filter.children: if isinstance(children, tuple): if children[0].startswith('data_begin'): continue elif isinstance(children, SearchNode): node_childrens = list(filter(lambda x: not x[0].startswith('data_begin'), children.children)) if node_childrens: children.children = node_childrens childrens.append(children) qs.query.query_filter.children = childrens self.extra_ctx['events_reversed'] = True qs.query.clear_order_by() return qs.filter(data_begin__lte=datetime.now()).order_by('-data_begin') def get_queryset(self): qs = super(ReverseOrderMixin, self).get_queryset() if isinstance(qs, SearchQuerySet) and qs.count() == 0: qs = self.get_reversed_sqs(qs) elif isinstance(qs, QuerySet) and not qs.exists(): qs = self.get_reversed_qs(qs) return qs