Skip to content

Commit

Permalink
Merge pull request #2281 from mccarthysean/fix-inline-model-AJAX-look…
Browse files Browse the repository at this point in the history
…up-to-use-new_name

reset the pre-configured loader's name to the "new_name"
  • Loading branch information
mrjoes committed Jan 3, 2023
2 parents 6b9cb37 + 8918be4 commit 836de8c
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 11 deletions.
73 changes: 62 additions & 11 deletions examples/sqla-custom-inline-forms/app.py
@@ -1,5 +1,6 @@
import os
import os.path as op
from pathlib import Path

from werkzeug.utils import secure_filename
from sqlalchemy import event
Expand All @@ -10,6 +11,7 @@
from wtforms import fields

import flask_admin as admin
from flask_admin.contrib.sqla.ajax import QueryAjaxModelLoader
from flask_admin.form import RenderTemplateWidget
from flask_admin.model.form import InlineFormAdmin
from flask_admin.contrib.sqla import ModelView
Expand Down Expand Up @@ -37,6 +39,22 @@ class Location(db.Model):
name = db.Column(db.Unicode(64))


class ImageType(db.Model):
"""
Just so the LocationImage can have another foreign key,
so we can test the "form_ajax_refs" inside the "inline_models"
"""
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64))

def __repr__(self) -> str:
"""
Represent this model as a string
(e.g. in the Image Type list dropdown when creating an inline model)
"""
return self.name


class LocationImage(db.Model):
id = db.Column(db.Integer, primary_key=True)
alt = db.Column(db.Unicode(128))
Expand All @@ -45,6 +63,9 @@ class LocationImage(db.Model):
location_id = db.Column(db.Integer, db.ForeignKey(Location.id))
location = db.relation(Location, backref='images')

image_type_id = db.Column(db.Integer, db.ForeignKey(ImageType.id))
image_type = db.relation(ImageType, backref='images')


# Register after_delete handler which will delete image file after model gets deleted
@event.listens_for(LocationImage, 'after_delete')
Expand Down Expand Up @@ -76,13 +97,26 @@ class CustomInlineModelConverter(InlineModelConverter):


# Customized inline form handler
class InlineModelForm(InlineFormAdmin):
class LocationImageInlineModelForm(InlineFormAdmin):
form_excluded_columns = ('path',)

form_label = 'Image'

# Setup AJAX lazy-loading for the ImageType inside the inline model
form_ajax_refs = {
"image_type": QueryAjaxModelLoader(
name="image_type",
session=db.session,
model=ImageType,
fields=("name",),
order_by="name",
placeholder="Please use an AJAX query to select an image type for the image",
minimum_input_length=0,
)
}

def __init__(self):
return super(InlineModelForm, self).__init__(LocationImage)
return super(LocationImageInlineModelForm, self).__init__(LocationImage)

def postprocess_form(self, form_class):
form_class.upload = fields.FileField('Image')
Expand All @@ -100,7 +134,7 @@ def on_model_change(self, form, model):
class LocationAdmin(ModelView):
inline_model_form_converter = CustomInlineModelConverter

inline_models = (InlineModelForm(),)
inline_models = (LocationImageInlineModelForm(),)

def __init__(self):
super(LocationAdmin, self).__init__(Location, db.session, name='Locations')
Expand All @@ -113,21 +147,38 @@ def index():
return render_template('locations.html', locations=locations)


if __name__ == '__main__':
# Create upload directory
try:
os.mkdir(base_path)
except OSError:
pass
def first_time_setup():
"""Run this to setup the database for the first time"""
# Create DB
db.drop_all()
db.create_all()

# Add some image types for the form_ajax_refs inside the inline_model
image_types = ("JPEG", "PNG", "GIF")
for image_type in image_types:
model = ImageType(name=image_type)
db.session.add(model)

db.session.commit()

return


# if __name__ == '__main__':
# Create upload directory
try:
os.mkdir(base_path)
except OSError:
pass

# Create admin
admin = admin.Admin(app, name='Example: Inline Models')

# Add views
admin.add_view(LocationAdmin())

# Create DB
db.create_all()
# Create DB
first_time_setup()

# Start app
app.run(debug=True)
6 changes: 6 additions & 0 deletions flask_admin/contrib/sqla/form.py
Expand Up @@ -636,6 +636,12 @@ def process_ajax_refs(self, info):
loader = create_ajax_loader(info.model, self.session, new_name, name, opts)
else:
loader = opts
# If we're changing the name in self.view._form_ajax_refs,
# we must also change loader.name property. Otherwise
# when the widget tries to set the 'data-url' property in the <input> tag,
# it won't be able to find the loader since it'll be using the "field.loader.name"
# of the previously-configured loader.
setattr(loader, "name", new_name)

result[name] = loader
self.view._form_ajax_refs[new_name] = loader
Expand Down

0 comments on commit 836de8c

Please sign in to comment.