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

Doesn't work anymore in the administration #8

Closed
debnet opened this issue Mar 21, 2018 · 12 comments
Closed

Doesn't work anymore in the administration #8

debnet opened this issue Mar 21, 2018 · 12 comments
Assignees

Comments

@debnet
Copy link

debnet commented Mar 21, 2018

TreeForeignKey and TreeManyToManyField doesn't work anymore inside the Django administration.
The field is rendered by values are always empty.

@debnet
Copy link
Author

debnet commented Mar 21, 2018

Mea culpa, it seems it affects only one model in my case, but I don't know why. I will reopen this issue if I have further informations.

@debnet debnet closed this as completed Mar 21, 2018
@debnet debnet reopened this Mar 21, 2018
@debnet
Copy link
Author

debnet commented Mar 21, 2018

I confirm. It seems to be a JavaScript issue after analysis. Data are well-gathered by the widget but it cannot render the result.

image

Treewidget specific JavaScript is loaded before Django Admin

<script type="text/javascript" src="/admin/jsi18n/"></script>
<link href="/static/treewidget/themes/default/style.css" type="text/css" media="screen" rel="stylesheet" />
<link href="/static/treewidget/default.css" type="text/css" media="screen" rel="stylesheet" />
<script type="text/javascript" src="/static/treewidget/prepare.js"></script>
<script type="text/javascript" src="/static/treewidget/jstree.min.js"></script>
<script type="text/javascript" src="/static/treewidget/default.js"></script>
<script type="text/javascript" src="/static/admin/js/vendor/jquery/jquery.js"></script>
<script type="text/javascript" src="/static/admin/js/jquery.init.js"></script>
<script type="text/javascript" src="/static/admin/js/core.js"></script>
<script type="text/javascript" src="/static/admin/js/admin/RelatedObjectLookups.js"></script>
<script type="text/javascript" src="/static/admin/js/actions.js"></script>
<script type="text/javascript" src="/static/admin/js/urlify.js"></script>
<script type="text/javascript" src="/static/admin/js/prepopulate.js"></script>
<script type="text/javascript" src="/static/admin/js/vendor/xregexp/xregexp.js"></script>
<script type="text/javascript" src="/static/admin/js/inlines.js"></script>
<script type="text/javascript" src="/static/admin/js/collapse.js"></script>

@jerch
Copy link
Collaborator

jerch commented Mar 21, 2018

Where are you using the treewidget? Normally model fields are instantiated in the adminmodel class, which delivers django.jQuery. The field assets get appended to these assets in the right order.

If you use treewidget before that or even on custom pages in the frontend, you need to deliver jquery by yourself. That is intended behavior to avoid cluttering the admin with different jquery versions.

@debnet
Copy link
Author

debnet commented Mar 21, 2018

Here the code:

Model

class Corporation(MPTTEntity, NamedModel, ContactModel):
    """
    Entreprise
    """
    parent = TreeForeignKey(
        'self', blank=True, null=True, on_delete=models.CASCADE, related_name='corporations',
        verbose_name=_("parent"))
    siren = models.CharField(
        max_length=14, blank=True,
        verbose_name=_("SIREN"))
    apen = models.CharField(
        max_length=5, blank=True,
        verbose_name=_("APEN"))
    extra_data = JsonField(
        blank=True, null=True, editable=False,
        verbose_name=_("autres données"))

    class Meta:
        verbose_name = _("entreprise")
        verbose_name_plural = _("entreprises")

    class MPTTMeta:
        order_insertion_by = ('name', )

Admin

class CorporationMixin:
    fieldsets = (
        (_("Général"), dict(
            fields=('name', 'description', 'parent',),
            classes=(),
        )),
        (_("Contact"), dict(
            fields=('phone', 'email', 'address', 'country', 'siren', 'apen',),
            classes=('collapse',),
        )),
        (_("Localisation"), dict(
            fields=('name_fr', 'name_en',),
            classes=('collapse',),
        )),
    )
    list_display_links = ('indented_title',)
    list_display = ('tree_actions', 'indented_title', 'parent', 'country',)
    list_filter = ()
    search_fields = ('name', 'description',)
    ordering = ()
    mptt_indent_field = 'name'


class CorporationInline(CorporationMixin, EntityStackedInline):
    """
    Filiales pour les entreprises
    """
    model = Corporation
    extra = 0


@admin.register(Corporation)
class CorporationAdmin(CorporationMixin, EntityAdmin, DraggableMPTTAdmin):
    """
    Administration des entreprises
    """
    inlines = (CorporationInline,)

None of the above have Media defined. By the way, as you can see, the JavaScript is included in the source, but before Django's...

@jerch
Copy link
Collaborator

jerch commented Mar 22, 2018

Ah ok, inline forms are always good to mix things up in admin - they get created beforehand therefore the wrong order of JS files.

@jerch jerch self-assigned this Mar 22, 2018
@debnet
Copy link
Author

debnet commented Mar 22, 2018

Disabling the inline doesn't seems to change anything. The treewidget medias are always loaded before Django Admin's one.

@debnet
Copy link
Author

debnet commented Mar 22, 2018

OK, found. When you have a fieldset with a collapse class, the admin goes crazy with treewidget. Remove all collapse solve the issue, but we have to deal with this.

@debnet
Copy link
Author

debnet commented Apr 4, 2018

Any ideas? 👍

@jerch
Copy link
Collaborator

jerch commented Apr 4, 2018

So the problem is only for fieldsets collapsed or fieldsets in general?

@debnet
Copy link
Author

debnet commented Apr 4, 2018

Works fine if there is no collapsible panel in the same page, doesn't work else.

@jerch
Copy link
Collaborator

jerch commented Apr 4, 2018

This is a tough one. The only workaround I was able to find is to load another jQuery beforehand. You can do this at the admin class:

class ExampleAdmin(admin.ModelAdmin):
    class Media:
        extend = False
        js = (
            'https://code.jquery.com/jquery-3.3.1.min.js',
        )

Note the extend = False - this is needed to force the loading of this asset before the fieldset stuff enters the stage. Also note this has a big drawback - the jQuery internal events are not seen by the other jQuery instance, therefore complicated jQuery stuff, which depends on events is not possible anymore.

A real solution would include some JS loading or bundling logic with only ONE jQuery instance, but the standard admin lacks both. No clue yet how to solve this. (Other admin projects simply inject their own jQuery, which is really cumbersome.)

@jerch
Copy link
Collaborator

jerch commented Mar 9, 2019

Closing this issue, the needed workaround is documented in the README.

@jerch jerch closed this as completed Mar 9, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants