lightarhont
@lightarhont
python/php developer

Как в 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


Есть вот такая вот вьюха...

Как добавить что бы в выборке при выборке фильтра по Городу, в select option были только Залы в этом городе, при фильтре по Залу?
  • Вопрос задан
  • 541 просмотр
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы