-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Add ability to use form_ajax_refs fields as editable columns #2164
Add ability to use form_ajax_refs fields as editable columns #2164
Conversation
@michaelbukachi would you mind checking my work here when you get the chance? Just want to make sure I'm seeing proper behavior in the areas I have marked as complete. And any insight on the ui glitches with the one-to-many would be appreciated :) I've been using the following minimal, reproducible example: from flask import Flask
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from flask_admin.contrib.sqla.ajax import QueryAjaxModelLoader
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config.update({
"SECRET_KEY": "NOTASECRET",
"SQLALCHEMY_DATABASE_URI": "sqlite:///:memory:",
"SQLALCHEMY_TRACK_MODIFICATIONS": False
})
db = SQLAlchemy(app)
class Structure(db.Model):
__tablename__ = "structures"
id = db.Column(db.Integer, primary_key=True) # noqa
name = db.Column(db.String(20), nullable=False) # noqa
power_unit_id = db.Column(db.Integer, db.ForeignKey("power_units.id")) # noqa
power_unit = db.relationship("PowerUnit", back_populates="structures") # noqa
def __str__(self):
"""Return a string representing the record."""
return self.name
class PowerUnit(db.Model):
__tablename__ = "power_units"
id = db.Column(db.Integer, primary_key=True) # noqa
name = db.Column(db.String(20), nullable=False) # noqa
structures = db.relationship("Structure", back_populates="power_unit") # noqa
def __str__(self):
"""Return a string representing the record."""
return self.name
class StructureView(ModelView):
"""Flask-Admin view for Structure model (structures table)"""
column_list = ("name", "power_unit")
form_columns = column_list
column_editable_list = form_columns
# test one-to-one
form_ajax_refs = {
"power_unit": QueryAjaxModelLoader(
"power_unit", db.session, PowerUnit, fields=["name"]
)
}
class PowerUnitView(ModelView):
"""Flask-Admin view for PowerUnit model (power_units table)"""
column_list = ("name", "structures")
form_columns = column_list
column_editable_list = form_columns
# test one-to-many - currently works on a db-level, but UI-wise it's a bit odd after selecting values from the select2 and saving/closing modal.
form_ajax_refs = {
"structures": QueryAjaxModelLoader(
"structures", db.session, Structure, fields=["name"]
)
}
# admin = Admin(app, url="/")
# admin = Admin(app, url="/", template_mode="bootstrap3")
admin = Admin(app, url="/", template_mode="bootstrap4")
admin.add_view(StructureView(Structure, db.session, name="Structures"))
admin.add_view(PowerUnitView(PowerUnit, db.session, name="Power Units"))
@app.before_first_request
def startup():
db.create_all()
num_items = 1000
for i in range(num_items):
db.session.add(Structure(name=f"a{i}b"))
db.session.add(PowerUnit(name=f"a{i}b"))
if i % 100 == 0 or i == num_items:
# commit once every 100 records and once we finish iterating
db.session.commit()
if __name__ == "__main__":
app.run(debug=True) |
@mrjoes As always, any thoughts from you are much appreciated as well :) |
@caffeinatedMike Testing as soon as I get home. |
select2: {
dropdownAutoWidth: true, // <------ This
minimumInputLength: $el.attr("data-minimum-input-length"),
placeholder: "data-placeholder", so the dropdown automatically adjusts the width according to the content. |
@michaelbukachi thanks for having a look. Will definitely address the width issue prior to merge. Any idea how I can solve the first issue? Where do I even start? The issue only seems to happen sporadically when there are multiple values (so when select2 multiple/tag mode). I'm kind of at a loss currently as to how to properly handle the field population/refresh. Another issue I noticed is when you add values through the inline edit and save, those values aren't available when clicking back into the field (so when the edit box is re-rendered) because |
@michaelbukachi I'm playing around with your PR locally. Hopefully, I'll have a solution during the day. |
@michaelbukachi thanks for tinkering, please let me know how you fare. I'm guessing when you say the state is not saved, you mean on the frontend (in data attributes of the html elements), correct? Because, just to be clear, the state does persist on the db-level (which can be seen by refreshing the page, it will show the updated tags). |
Yes. I'm referring to state on the frontend. |
@michaelbukachi I think I might know what I'm missing. I think we might just need to add another setting to
I won't be able to test this theory until tomorrow morning though. If you have the free-time and feel like it, feel free to do so :) |
@caffeinatedMike In which file are we adding this? |
This would go in |
Alright. Let me test it. |
@michaelbukachi I've fixed the issue with values that are added in the select2 box not persisting back to the table *Edit: Scratch that, select2 multiple only populates with values from the table after refresh. Need to figure out proper way to update |
@caffeinatedMike Noice! I was barely able to play around with the PR today. Glad to see you are making progress. |
@michaelbukachi done! please review latest changes and let me know if everything works as expected :) |
@caffeinatedMike LG! However, when I select a relationship object which has already been selected, it doesn't deselect it from the the other selected row. See the GIF below: |
@michaelbukachi Ahh, thanks for reminding me/pointing that out! I forget to include one of my local snippets that addressed that. Will update sometime today and re-ping you. |
@michaelbukachi Sorry for falling off the map. It's been a long and tough week since my last comment. I finally have some time tomorrow to have a look at this. |
No pressure 🙂 |
…ues into an array
…n open if multiple.
…om select2 modal (without refresh)
@michaelbukachi I'm just getting back to this. After looking at this, it seems it's a matter of determining the type of relationship (one-to-one or one-to-many). In the case of the Structures column at the I just can't see in the code where I can inject the additional data-attribute. Thoughts? |
@mrjoes You might be the better party to ask here since you built the internals. How could I go about passing on the type of relationship an Ajax field is dealing with? Ideally I'd like to add it as a data-attribute to the editable field, so we would ultimately need to pass this value to the xeditable widget, right? The question is how? |
@caffeinatedMike sorry for the late response. This slipped my mind so I forgot to take a look. I'll take a look at it during the day. |
Are fields aware of the data they hold? Or the models they are associated with? If we are able to determine the type as |
@michaelbukachi Only while they're first being instantiated (example: within After reviewing the codebase a little more, I believe the spot that is the most ideal for determining this is the |
I see. Maybe we can introduce enums for the relationship types and pass them around? |
My thoughts exactly, but haven't figured out where I can inject the data-attribute value other than on the widget level. |
@michaelbukachi |
@caffeinatedMike I've been playing with the widgets. I'm actually really close to figuring it out. |
Awesome! Please let me know your findings when you do. This has been a thorn in my side for a bit. It's really the only thing stopping this PR from being merge-ready. Starting Monday I'll be able to focus my attention on Flask-Admin full-time as I'm building the next iteration of a management portal I've created for my day job. So, if you figure it out before then it'll give a great starting point come Monday. |
@caffeinatedMike We might not have to go through overriding |
@michaelbukachi It is. However, what we need is the opposite relationship. For example, in the code we're using to test: form_ajax_refs = {
"structures": QueryAjaxModelLoader(
"structures", db.session, Structure, fields=["name"]
)
} What we need is to determine the |
@caffeinatedMike it should be possible to get the relationship model and infer relationship direction from there if we don't want to assume direct opposites. |
@michaelbukachi That's what I've been trying to figure out: where to do this and how to ultimately pass it onto the widget kwargs. |
|
|
This is where we should determine the relationship and return the appropriate widget which has the relationship information. |
Yes, that's why I was pointing it out... I'm currently tied up with a few other tasks at the moment, but putting it here as a note. |
@caffeinatedMike so checking |
@michaelbukachi I'm just getting back to this now. The logic you provided doesn't really help when looking at the example code we've been using to test. In your gif of the bug each PowerUnit can have many Structures, but a Structure can only belong to a single PowerUnit at a time. So, just because the inline field is a SelectMultiple that doesn't mean we can assume that values should remain. The values should only remain in MANYTOMANY (our situation deals with a ONETOMANY). Net net, we need a different way to determine this because using just the field type of SelectMultiple as an indicator would mean having a 50% chance of getting it wrong. |
@caffeinatedMike what needs to be displayed for |
@michaelbukachi nothing needs to be done for MANYTOMANY. For ONETOMANY we need some sort of data-attribute to let us know to iterate over all other entries present on the current page in order to remove the value from those. In your gif you point out how the element is not removed from the other field until refresh. Thats the situation where we need a signifier to tell us to do this via JS to avoid needing a page refresh. |
FYI, I didn't realize you had done this already. I added the same functionality in the following pull request: |
Adds support for inline-editing of form_ajax_refs fields from
list.html
views:🐛 Both single and multi-select inline editable fields are unable to have all their values deleted from the inline select2 field in the
list.html
views. This relates to #357 and should be addressed when that issue is addressed.Closes #1627
Closes #2063
Closes #2157