M
M
Mikhail2020-01-07 12:10:46
Python
Mikhail, 2020-01-07 12:10:46

How to double filter in Flask-Admin?

import os
import subprocess

from flask_admin.contrib.sqla import ModelView
from app.admin.helpers.files_validation_helper import ImageRequired
from app.admin.mixins.models_views import BaseModelView
from app.admin.helpers.widgets_helper import CustomTimeField
from app.admin.helpers.widgets_helper import MultipleSelect2Field
from app.admin.helpers.week_days import weekly_days_numbers, WEEKLY_EVENT_DAYS
from app.admin.helpers.widgets_helper import FileUploadFieldOverride
from app.helpers.file_ops import get_uuid_hash
from app.helpers.file_ops import normalize_filename
from app.helpers.file_ops import write_file

from app.models.weekly_event import WeeklyEventDays, WeeklyEvent
from app.models.schedule import Schedule
from app.models.gym import Gym
from app.models.gym_city import GymCity
from app.models.user import User

from config.environments.settings import AVATARS_UPLOAD_PATH
import datetime
from app.admin.helpers import time_plus
from wtforms.validators import ValidationError
from db.sessions import Session
from sqlalchemy import or_
from flask_admin.contrib.sqla.filters import BaseSQLAFilter
from flask import Markup


class FilterByUser(BaseSQLAFilter):
    
    
    def apply(self, query, value, alias=None):
        return query.join(User, WeeklyEvent.user_id == User.id, aliased=True) \
        .filter(User.name.in_(value.split(',')))
    
    
    def operation(self):
        return u'выбрать'
    
    
    def get_options(self, view):
        return [(p.name, p.name) for p in Session.query(User).order_by(User.id)]


class FilterByCity(BaseSQLAFilter):
    
    
    def apply(self, query, value, alias=None):
        return query.join(Gym, WeeklyEvent.gym_id == Gym.id) \
        .join(GymCity, Gym.gym_city_id == GymCity.id) \
        .filter(GymCity.name.in_(value.split(',')))
    
    
    def operation(self):
        return u'выбрать'
    
    
    def get_options(self, view):
        return [(p.name, p.name) for p in Session.query(GymCity).order_by(GymCity.id)]

class FilterByGym(BaseSQLAFilter):
    
    def apply(self, query, value, alias=None):
        return query.join(Gym, WeeklyEvent.gym_id == Gym.id, aliased=True) \
        .filter(Gym.name.in_(value.split(',')))
    
    
    def operation(self):
        return u'выбрать'
    
    
    def get_options(self, view):
        return [(p.name, p.name) for p in Session.query(Gym).order_by(Gym.id)]

class WeeklyEventModelView(BaseModelView, ModelView):

    form_overrides = dict(
        weekly_event_start_time=CustomTimeField,
        weekly_event_end_time=CustomTimeField,
        weekly_event_days=MultipleSelect2Field,
        image_url=FileUploadFieldOverride,
    )
    form_args = dict(
        image_url=dict(validators=[ImageRequired()])
    )

    column_list = (
        'title',
        'city',
        'gym',
        'weekly_event_start_time',
        'weekly_event_end_time',
        'weekly_event_days',
        'user',
    )
    column_default_sort = ('id', True)
    form_excluded_columns = ('record_modified', 'record_created')
    can_delete = True
    can_create = True
    page_size = 50
    column_filters = ('title', 'record_created', )
    column_labels = {
        'title': 'Заголовок',
        'gym': 'Зал',
        'city': 'Город',
        'weekly_event_start_date': 'Начало события',
        'weekly_event_stop_date': 'Конец события',
        'weekly_event_days': 'Дни по которым проходит событие',
        'weekly_event_start_time': 'Время начала события',
        'weekly_event_end_time': 'Время завершения события',
        'description': 'Описание',
        'user': 'Пользователь',
        #'image_url': 'Изображение',
    }

    form_columns = [
        'title',
        'gym',
        'user',
        'weekly_event_start_date',
        'weekly_event_stop_date',
        'weekly_event_days',
        'weekly_event_start_time',
        'weekly_event_end_time',
        'description',
        #'image_url',
    ]

    form_args = dict(
        weekly_event_start_time=dict(default_format='%H:%M', formats=('%H:%M', '%H:%M')),
        weekly_event_end_time=dict(default_format='%H:%M', formats=('%H:%M', '%H:%M')),
        weekly_event_days=dict(render_kw=dict(multiple="multiple"), choices=[(v, v) for v in WEEKLY_EVENT_DAYS])
    )

    column_formatters = dict(
        weekly_event_start_time=lambda v, c, m, p: m.clock_start.strftime('%H:%M'),
        weekly_event_end_time=lambda v, c, m, p: m.clock_end.strftime('%H:%M')
    )

    form_widget_args = {
        form_column: {'autocomplete': "off"}
        for form_column in form_columns
    }
    
    column_filters = [
        FilterByCity(column=None, name='Фильтр по городу'),
        FilterByGym(column=None, name='Фильтр по залу'),
        FilterByUser(column=None, name='Фильтр по пользователю'),
    ]
    
    def get_city(view, context, model, name):
        l = model.gym.gym_city.name
        return Markup(l)
    
    def get_gym(view, context, model, name):
        l = model.gym.name
        return Markup(l)
    
    #def get_type(view, context, model, name):
    #    return STATUSES[model.type][1]
    
    column_formatters = {
       'city': get_city,
       'gym': get_gym,
    #   'type': get_type
    }
    
    '''
    def _change_path_data(self, _form):
        if not _form.image_url.raw_data:
            return

        storage_file = _form.image_url.raw_data[0]

        if not hasattr(storage_file, 'filename'):
            return

        filename = os.path.basename(storage_file.filename)
        uuid_hash = get_uuid_hash(filename)
        uuid_folder = '{}{}'.format(AVATARS_UPLOAD_PATH, uuid_hash)

        fpath = "{}/{}".format(
            uuid_folder,
            normalize_filename(filename)
        )

        source_url = os.path.join(uuid_hash, normalize_filename(filename))

        try:
            if not os.path.exists(uuid_folder):
                try:
                    os.makedirs(uuid_folder)
                except Exception as e:
                    subprocess.call(['chmod', '-R', '+w', uuid_folder])
                    os.makedirs(uuid_folder)

            write_file(fpath, _form.image_url.data)
        except Exception:
            raise

        source_url = '/images/%s' % source_url
        return source_url

    def on_model_change(self, form, model, is_created):
        image_url = self._change_path_data(form)
        if image_url:
            model.image_url = image_url
    '''
    
    def on_model_change(self, form, model, is_created):
        
        if form.weekly_event_start_date.data > form.weekly_event_stop_date.data:
            raise ValidationError('Дата начала не может быть меньше даты конца')
        
        if form.weekly_event_start_time.data > form.weekly_event_end_time.data:
            raise ValidationError('Время начала не может быть меньше времени конца')
        
        td = datetime.timedelta(minutes=1)
        tstart = time_plus(form.weekly_event_start_time.data, td)
        tstop = time_plus(form.weekly_event_end_time.data, td)
        
        l = weekly_days_numbers(form.weekly_event_days.data)
        delta = datetime.timedelta(days=1)
        d1 = form.weekly_event_start_date.data
        d2 = form.weekly_event_stop_date.data
        days_in_period = []
        while d1 <= d2:
                if d1.weekday() in l:
                    days_in_period.append(d1)
                d1 += delta
        
        c = Session.query(Schedule).join(Gym, Gym.id == Schedule.gym_id) \
        .filter(Gym.id == form.gym.data.id) \
        .filter(Schedule.date.in_(days_in_period)) \
        .filter(or_(Schedule.time_start.between(tstart, tstop),
                    Schedule.time_stop.between(tstart, tstop)))
        
        if c.count() != 0:
            raise ValidationError('В расписании уже найдено событие на это время! Укажите другое время или дату!')
        
        cw = Session.query(WeeklyEvent).join(Gym, Gym.id == WeeklyEvent.gym_id) \
        .join(WeeklyEventDays, WeeklyEvent.id == WeeklyEventDays.weekly_event_id) \
        .filter(Gym.id == form.gym.data.id) \
        .filter(WeeklyEventDays.event_date.in_(days_in_period)) \
        .filter(or_(WeeklyEvent.weekly_event_start_time.between(tstart, tstop),
                    WeeklyEvent.weekly_event_end_time.between(tstart, tstop)))
        
        if not is_created:
            cw = cw.filter(WeeklyEvent.id != model.id)
        
        if cw.count() != 0:
            raise ValidationError('В расписании уже найдено периодическое событие на это время! Укажите другое время или дату!')
                
        #Добавляем даты в периоде        
        for e in days_in_period:
            wed = WeeklyEventDays(event_date=e)
            model.days_in_period.append(wed)
        
    
    def edit_form(self, obj=None):
        form = super(WeeklyEventModelView, self).edit_form(obj)
        return form

    def edit_form(self, obj):
        return self._customize_form(super(WeeklyEventModelView, self).edit_form(obj), obj)

    def _customize_form(self, form, model=None):
        return form

There is such a view here ...
How to add that in the selection when filtering by City, in the select option there were only Halls in this city, when filtering by Hall?

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question