user6089877
user6089877

Reputation:

How to access to the model through a ModelMultipleChoiceField

I have a ManytoMany field in my model called 'games' :

class Team(models.Model):
    name = models.CharField(max_length=15, null=False)
    tag = models.CharField(max_length=4, null=False)
    description = HTMLField(blank=True, null=True)
    logo = models.FileField(upload_to=user_directory_path, validators=[validate_file_extension], blank=True, null=True)
    games = models.ManyToManyField(Games, verbose_name="Jeu", null=False)
    owner = models.ForeignKey(User, verbose_name="Créateur")
    date = models.DateTimeField(auto_now_add=True, auto_now=False, verbose_name="Date de création")
    update = models.DateTimeField(auto_now=True, verbose_name="Dernière modification")

    def __str__(self):
        return self.name

I feed my model thanks to a form :

class TeamForm(forms.ModelForm):
    game = Games.objects.all()
    games = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple, required=True, queryset=game)
    logo = forms.ImageField(required=False, widget=forms.FileInput)
    class Meta:
        model = Team
        fields = ('name', 'tag', 'description', 'logo', 'games' )

In my other Model 'Games', I have a field 'logo'. I can show you :

class Games(models.Model):
    guid = models.CharField(max_length=100, unique=True, null=False, verbose_name="GUID")
    title = models.CharField(max_length=100, null=False, verbose_name="Titre")
    logo = models.FileField(upload_to='media/games/', validators=[validate_file_extension], blank=True, null=True, verbose_name="Logo du jeu")
    date = models.DateTimeField(auto_now_add=True, auto_now=False, verbose_name="Date de création")
    update = models.DateTimeField(auto_now=True, verbose_name="Dernière modification")
    def __str__(self):
        return self.title

So, in my template I can render the ManytoMany field by calling {{form.games}}. It works fine, I have a lots of checkbox just next to the name of any games.

Now, to make more cool, I also want to display the logo of the game. The problem is that the ModelMultipleChoiceField only return the game.id and the game.title. It only returns a tuple by default.

So I am blocked...

If you have any idea, or if someone already had the same issue, please give me a solution.

Many thanks

For more details, I add my views.py and my template :

def view_team_index(request, id_team):
    if request.user.is_authenticated():
        media = settings.MEDIA

        try:
            team = Team.objects.get(id=id_team)

            if team.owner == request.user:
                owner = True
        except:
            messages.add_message(request, messages.INFO, 'Désolé, cette équipe est inconnue.')
            return redirect(view_wall)

        if request.method == 'POST':

            #Modifier les champs de bases
            if 'request_base' in request.POST:
                request_base = True
                form = TeamForm(instance=Team.objects.get(owner=request.user))

            if 'edit_base' in request.POST:
                form = TeamForm(request.POST, request.FILES, instance=Team.objects.get(owner=request.user))
                if form.is_valid():
                    form.save()

            #Modifier la description
            if 'request_description' in request.POST:
                request_description = True
                form = TeamForm()

            if 'edit_description' in request.POST:
                form = TeamForm(request.POST)

        return render(request, 'team_index.html', locals())
    else:
        messages.add_message(request, messages.INFO, 'Vous devez être connecté pour accéder à cette page.')
        return redirect(view_logon)

template :

<div class="w3-row-padding margin_bottom_10">
    <div class="w3-col m12">
        <div class="w3-card-2 w3-round w3-white">
            <div class="w3-container w3-padding container">
                <div class="text_align_center">
                    <div><img src="{{media}}{{team.logo|default:"media/images/avatar-default-blue.png"}}" class="w3-circle logo_team" alt="Logo"></div>
                    <form method="POST" action="" enctype="multipart/form-data">
                        {% csrf_token %}
                        <hr>
                        {{form.logo}}
                        <hr>
                        <h4>{{form.name}}</h4>
                        {{form.tag}}
                        <hr>
                        <div class="list">{{form.games}}</div>
                        <hr>
                        <input type="submit" value="Sauvegarder" name="edit_base" class="w3-button w3-theme-d2 w3-margin-bottom">
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

I tried to override the choice field, but I have an error :

games = forms.GamesChoiceField(widget=forms.CheckboxSelectMultiple, required=True, queryset=game)
AttributeError: module 'django.forms' has no attribute 'GamesChoiceField'

This is what I tried :

from django.forms import ModelMultipleChoiceField

class GamesChoiceField(ModelMultipleChoiceField):
    def label_from_instance(self, obj):
        logo = '<img src="{url}"/>'.format(url=obj.logo.url)
        return "{title} {logo}".format(title=obj.title, logo=logo)

class TeamForm(forms.ModelForm):
    game = Games.objects.all()
    games = forms.GamesChoiceField(widget=forms.CheckboxSelectMultiple, required=True, queryset=game)
    logo = forms.ImageField(required=False, widget=forms.FileInput)
    class Meta:
        model = Team
        fields = ('name', 'tag', 'description', 'logo', 'games' )

Let's see my current working situation. I just want to add games.logo next to each checkbox. enter image description here

Upvotes: 1

Views: 878

Answers (2)

user8060120
user8060120

Reputation:

You can try to override ModelMultipleChoiceField

from django.forms import ModelMultipleChoiceField


class GamesChoiceField(ModelMultipleChoiceField):
    def label_from_instance(self, obj):
        logo = '<img src="{url}"/>'.format(url=obj.logo.url)
        return "{title} {logo}".format(title=obj.title, logo=logo)


class TeamForm(forms.ModelForm):
    game = Games.objects.all()
    games = GamesChoiceField(widget=forms.CheckboxSelectMultiple, required=True, queryset=game)

more details fields-which-handle-relationships

hope it help

extra info replace

games = forms.GamesChoiceField
#     ^^^^

to

games = GamesChoiceField
#     ^^^^

Upvotes: 2

wololoooo
wololoooo

Reputation: 134

You have to render the choice field manually. Replacing {{form.games}} by this:

<select name="games">
    {% for game in form.games_set.all %}
        <option value="{{ game.id }}">{{ game.logo }}</option>
    {% endfor %}
</select>

The code is aprox. But the concept is that. Perhaps you want to use jquery/bootstrap to beautify.

Another subject it's the related name in your ManyToManyField, if you want to custom your reverse lookup, set it up.

Upvotes: -1

Related Questions