Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass model instead of id to on_form_prefill #2292

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

kanhebei
Copy link
Contributor

on_form_prefill
I think it is more reasonable to change the second parameter to model

on_form_prefill
I think it is more reasonable to change the second parameter to model
@hasansezertasan
Copy link
Member

What was the application that motivated you to change it this way?

@kanhebei
Copy link
Contributor Author

What was the application that motivated you to change it this way?
The model has more data than the ID

@hasansezertasan
Copy link
Member

It looks OK to me if the tests are passing. Do you have any comment on this one @samuelhwilliams?

@hasansezertasan
Copy link
Member

I believe you also need to update lines between 1643 and 1644 @kanhebei.

@hasansezertasan hasansezertasan changed the title Update base.py Pass model instead of id to on_form_prefill Jul 20, 2024
@samuelhwilliams
Copy link
Contributor

Agree @hasansezertasan - we'd need to update the signature of the on_form_prefill method and the docstring at the very least.

It would be great if you could explain your use-case just to help us understand why you need this :)

@kanhebei
Copy link
Contributor Author

image

@kanhebei
Copy link
Contributor Author

from collections import OrderedDict

from flask import request, g, flash, redirect
from flask_admin import expose
from flask_admin.actions import action
from flask_admin.helpers import get_form_data
from flask_admin.form import BaseForm
from flask_login import current_user
from wtforms.fields import SelectField, SelectMultipleField, StringField

from application.contrib.admin import admin, ModelView
from application.contrib import db

from application.models import (
    Product,
    ProductCategory,
    ProductOptionAssociation
)

common_column_labels = {
    'created_at': '创建时间',
    'updated_at': '更新时间',
}


class MyBaseForm(BaseForm):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # 对 self._fields 重新排序
        new_fields = OrderedDict()
        field_order = getattr(self, '_fields_order', [])
        for name in field_order:
            new_fields[name] = self._fields.pop(name)
        for key, value in self._fields.items():
            new_fields[key] = value
        self._fields = new_fields


@admin.register(
    Product,
    db.session,
    endpoint='admin_product',
    url='product',
    name="产品管理"
)
class ProductModelView(ModelView):
    can_view_details = True
    details_modal = True
    # can_delete = False
    can_export = True
    column_editable_list = ['price_internal', 'price_selling', 'status', 'good']
    column_list = ['category', 'title', 'price_internal', 'price_selling', 'good', 'status', 'created_at']
    form_columns = ['price_internal', 'price_selling', 'title', 'case', 'status', 'good', 'keyword', 'remark']
    column_details_exclude_list = ['options', 'refuse']
    column_labels = {
        'user': '用户',
        'title': '产品名称',
        'category': '产品分类',
        'category_id': '产品分类',
        'case': '案例',
        'keyword': '关键词',
        'remark': '备注说明',
        'price_selling': '售价',
        'price_internal': '底价',
        'attribute_option': '属性项',
        'status': '状态',
        'good': '推荐',
        **common_column_labels
    }

    column_choices = {
        'status': Product.status.info['choices'],
    }

    form_choices = column_choices

    can_set_page_size = True
    column_default_sort = ('pk', True)
    column_searchable_list = ['title']
    column_filters = ['category_id', 'status']

    column_sortable_list = ['price_selling', 'price_internal']

    create_template = 'admin/product/create.html'

    # 使用自定义的表单类,,并重写了__init__ 已实现表单字典的自定义顺序排列
    form_base_class = MyBaseForm

    @staticmethod
    def _attributes_field(category: ProductCategory):
        attrs = {}
        # 当前分类的属性
        for attr in category.attributes:
            kwargs = {
                'label': attr.title,
                'choices': [
                    (option.pk, option.title)
                    for option in attr.options
                ],
                'coerce': int
            }
            # 生成对应属性的表单字段
            attrs[f'attribute-{attr.pk}'] = SelectMultipleField(**kwargs) if attr.multiple else SelectField(**kwargs)

        return attrs

    def edit_form(self, obj=None):
        _fields = self._attributes_field(obj.category)
        # form_base_class 为自定义的表单类,,然后给出 _fields_order
        # 根据_fields_order列表序列排序
        _fields['_fields_order'] = ['readonly__category', 'title', *list(_fields.keys())]
        _fields['readonly__category'] = StringField(
            '产品分类',
            render_kw={'readonly': True},
            default=obj.category
        )
        form_class = type('form_class', (self._create_form_class,), _fields)
        return form_class(get_form_data(), obj=obj)

    def create_form(self, obj=None):
        _fields = self._attributes_field(g.current_product_category)
        # form_base_class 为自定义的表单类,,然后给出 _fields_order
        # 根据_fields_order列表序列排序
        _fields['_fields_order'] = ['category_id', 'title', *list(_fields.keys())]
        _fields['category_id'] = SelectField(
            '产品分类',
            choices=[
                (_.pk, _.title) for _ in ProductCategory.query.all()
            ],
            coerce=int,
            default=g.current_product_category.pk,
            description='产品分类确定后不可更变'
        )
        form_class = type('form_class', (self._create_form_class,), _fields)
        return form_class(get_form_data(), obj=obj)

    @expose('/new/', methods=('GET', 'POST'))
    def create_view(self):
        cid = request.args.get('category_id', default=0, type=int)
        pc = ProductCategory.query.get(cid) or ProductCategory.query.first()
        if pc is None:
            flash('产品分类不存在,请先创建分类', 'error')
            return redirect(self.get_url('.index_view'))
        g.current_product_category = pc
        return super().create_view()

    def on_model_change(self, form, model: Product, is_created):
        options = []
        for f in form:
            if f.name.startswith('attribute-'):
                _, attr_id = f.name.split('attribute-')
                if f.type == 'SelectMultipleField':
                    for data in f.data:
                        options.append({
                            'attribute_id': int(f.name.split('attribute-')[1]),
                            'option_id': data
                        })
                else:
                    options.append({
                        'attribute_id': f.name.split('attribute-')[1],
                        'option_id': f.data
                    })

        if is_created:
            for option in options:
                model.options.append(
                    ProductOptionAssociation(
                        category_id=model.category_id,
                        attribute_id=option['attribute_id'],
                        option_id=option['option_id']
                    )
                )
        else:
            for option_obj in model.options:
                db.session.delete(option_obj)
            db.session.flush()
            # # 添加新的关联关系
            for option in options:
                model.options.append(
                    ProductOptionAssociation(
                        category_id=model.category_id,
                        attribute_id=option['attribute_id'],
                        option_id=option['option_id']
                    )
                )

        # 应华总要求,取消默认价格自增1的功能
        if is_created:
            if not model.price_selling:
                # model.price_selling = model.price_internal +
                model.price_selling = model.price_internal
            model.user = current_user
        # else:
        #     if model.price_selling <= model.price_internal:
        #         # model.price_selling = model.price_internal + 1
        #         model.price_selling = model.price_internal

    def on_form_prefill(self, form, model: Product):
        for attr in model.category.attributes:
            f = getattr(form, f'attribute-{attr.pk}')
            # 栏目属性可为多值
            if attr.multiple:
                values = []
                for option in model.options:
                    if option.attribute_id == attr.pk:
                        values.append(option.option_id)

                f.data = values
            else:
                for option in model.options:
                    if option.attribute_id == attr.pk:
                        f.data = option.option_id

    def _get_filter_groups(self):
        filter_groups = super()._get_filter_groups()
        options = [(item.pk, item.title) for item in ProductCategory.query.all()]
        for index, item in enumerate(filter_groups['产品分类']):
            filter_groups['产品分类'][index]['options'] = options
        return filter_groups

@samuelhwilliams
Copy link
Contributor

I'm happy to accept this PR in principle, but it is currently incomplete.

You'll need to:

  • update docstrings
  • update the method signature itself
  • update any other places that call this method
  • update/fix/add any tests

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

None yet

3 participants