ivica
ivica

Reputation: 1426

Django choices with model objects

Yes, this is an assignment, and yes, I've spent some time on it and now I need help.

My task has two models, Server and Client and they are in 1-N relationship. as noted below

# models.py
class Server(models.Model):
    name = models.CharField(max_length=255, unique=True, null=False, blank=False)
    maximum_clients = models.IntegerField(default=1,null=False, blank=False)

class Client(models.Model):
    name = models.CharField(max_length=255, unique=True, null=False, blank=False)
    active = models.BooleanField(default=True)
    server = models.ForeignKey(Server)

I have created a form with ModelForm which allows me to create a new client on a given server, but the prerequisite of the task is to only offer servers which have free capacity (their maximum_clients is less than actual clients) so this is what I did

#forms.py
from django.db.models import Count

qs = Server.objects.annotate(Count('client'))
server_choices = []
for server in qs:
    if server.client__count < server.maximum_clients:
        server_choices.append((server,server))

class ClientForm(forms.ModelForm):
    name = forms.CharField(label='Client name')
    server = forms.ChoiceField(choices=server_choices)
    class Meta:
        model = Client
        fields = ('name','server',)

This approach populates the select with the right servers based on a precondition that I mentioned. However, saving this form produces an error like Cannot assign "u'fifty'": "Client.server" must be a "Server" instance. Fifty is the name of the server with maximum_clients = 50

There is a similar form on admin screens which I also modified to show only available servers and saving there produces the same error.

Upvotes: 1

Views: 4268

Answers (1)

Daniel Roseman
Daniel Roseman

Reputation: 600051

This is not the right approach. Apart from the error you are seeing, you will also find that your server_choices only update when you restart the webserver, rather than doing so whenever the Server objects themselves change.

You have a foreign key, and need to select from a subset of the related objects. The correct field for that is a ModelChoiceField; this takes a queryset which you can filter in the definition. Since your filter depends on a field in the same model, you need to use an F object.

class ClientForm(forms.ModelForm):
    name = forms.CharField(label='Client name')
    server = forms.ModelChoiceField(
        queryset=Server.objects.annotate(client_count=Count('client')).filter(client_count__lt=F('maximum_clients')
    )

Upvotes: 2

Related Questions