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

Error: TypeError unhashable type: 'list' #311

Open
mathias4github opened this issue Aug 3, 2020 · 5 comments
Open

Error: TypeError unhashable type: 'list' #311

mathias4github opened this issue Aug 3, 2020 · 5 comments

Comments

@mathias4github
Copy link

Getting the same issue, the workaround is not working for me. My model ist not using any ReadOnly Fields.

destination = CountryField(default='DE', multiple=True)

Request Method: | GET
-- | --
/admin/products/shippingproduct/
3.0.8
TypeError
unhashable type: 'list'
/mpc/env/lib/python3.7/site-packages/django/contrib/admin/utils.py in display_for_field, line 382

Sees to be a problem here:

`

Variable Value
_boolean_icon <function _boolean_icon at 0x7ff8cb481510>
empty_value_display '-'
field <django_countries.fields.CountryField: destination>
value [Country(code='DE')]

`

Originally posted by @mathias4github in #298 (comment)

@jedie
Copy link
Contributor

jedie commented Aug 21, 2020

I ran into the same problem and can't use the work-a-round from #298 (comment)

@browniebroke
Copy link

browniebroke commented Oct 6, 2020

I have the same problem as well. I haven't tested it yet, but in case you're using PostgreSQL, an alternative option to using CountryField(multiple=True) might be to use an ArrayField.

The example in the readme would look like this (I think):

from django.contrib.postgres.fields import ArrayField
from django.db import models
from django_countries import countries
from django_countries.fields import CountryField


class Incident(models.Model):
    title = models.CharField(max_length=100)
    countries = ArrayField(
        base_field=CountryField(),
        size=len(countries),
    )

I guess this would work better out of the box with Django. Anyone tried this approach before?

@jedie
Copy link
Contributor

jedie commented Feb 23, 2021

I ran into the same problem and can't use the work-a-round from #298 (comment)

I made a work-a-round for Admin inlines. Looks like:

class DjangoCountriesInlineReadOnlyUserFixMixin:
    def countries_readonly(self, obj):
        return ', '.join(country.name for country in obj.countries)
    countries_readonly.short_description = _('Countries')

    def has_parent_change_permission(self, request):
        parent_model = self.parent_model
        opts = parent_model._meta
        codename = get_permission_codename('change', opts)
        return request.user.has_perm(f'{opts.app_label}.{codename}')

    def get_fields(self, request, obj=None):
        fields = super().get_fields(request, obj)

        can_change = (
            self.has_change_permission(request) and self.has_parent_change_permission(request)
        )
        if not can_change:
            fields.remove('countries')
            self.readonly_fields = list(self.readonly_fields) + ['countries_readonly']

        return fields

class FooBarInline(DjangoCountriesInlineReadOnlyUserFixMixin, admin.TabularInline):
    model = FooBarModel # this model has the 'countries' field
    #...

The idea is to replace the model field (in this example: counties) from the form.

@philgyford
Copy link
Contributor

I have a similar problem but can't yet figure out a solution. For my ModelAdmin I want to add countries to readonly_fields – it's never user-editable on this model – but I'm stumped. I've tried adapting the above but the get_fields() method never seems to be called when rendering my change page, from what I can tell.

@philgyford
Copy link
Contributor

While doing something else I figured out the solution.

models.py:

class MyModel(models.Model):
    countries = CountryField(multiple=True, blank=True)

admin.py:

from django.contrib import admin
from django.utils.html import format_html_join
from .models import MyModel

@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
    fields = ("get_countries",)
    readonly_fields = ("get_countries",)

    @admin.display(description="Countries")
    def get_countries(self, obj):
        return format_html_join(
            ", ",
            "{}",
            ((country.name,) for country in obj.countries),
        )

Screenshot 2022-12-05 at 11 36 04

That example doesn't really need format_html_join() but you could instead show the flags:

    @admin.display(description="Countries")
    def get_countries(self, obj):
        return format_html_join(
            " ",
            "<span title='{}'>{}</span>",
            ((country.name, country.unicode_flag) for country in obj.countries),
        )

Screenshot 2022-12-05 at 11 37 10

(mouse cursor not shown in screenshot)

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

No branches or pull requests

5 participants