tonino.j
tonino.j

Reputation: 3871

Dynamical choices in model's field

I want my models to have order field, which will contain order of an item among all items of its kind.

And I want to use choices within that IntegerField, which would contain all the numbers of currently existing items in that table.

So it would need to be dynamic choices.

How do I load all existing "order" values of all existing items in a table, and use this list for choices?

Upvotes: 0

Views: 415

Answers (2)

Thierry Lam
Thierry Lam

Reputation: 46294

It sounds like you want to build a manager for your model:

models.py

from django.db import models


class OrderManager(models.Manager):

    def order_choices(self):
        return [(i, i) for i in OrderModel.objects.values_list('order', flat=True)]


class OrderModel(models.Model): 

    objects = OrderManager()
    order = models.IntegerField()

    class Meta:
        ordering = ['order'] 

    def __unicode__(self):
        return '%i' % self.order

forms.py

from django import forms

from yourapp.models import OrderModel


class OrderModelForm(forms.ModelForm):
    order = forms.ChoiceField(choices=OrderModel.objects.order_choices())

    class Meta:
        model = OrderModel

admin.py

from django.contrib import admin

from yourapp.forms import OrderModelForm
from yourapp.models import OrderModel


class OrderModelAdmin(admin.ModelAdmin):

    form = OrderModelForm


admin.site.register(OrderModel, OrderModelAdmin)

Edit

Managers are use to make general model queries without having an instance of a model object. If you don't understand the concept of managers, you can still refactor the code out of the manager class, stick it somewhere else and import that function across your code. Managers allow you to abstract custom general queryset that you can reuse. See more details https://docs.djangoproject.com/en/dev/topics/db/managers/

The code without the manager will look like

views.py or some other file

from app.models import OrderModel

def order_choices():
    return [(i, i) for i in OrderModel.objects.values_list('order', flat=True)]

From anywhere in your code, if you want to reuse the above multiple times:

from app.views import oder_choices

order_choices()

as opposed to:

from app.models import OderModel

OrderModel.objects.order_choices()

If you only want to use the above once, you can leave it in the forms.py as shown in the other answer. It's really up to you on how you want to refactor your code.

Upvotes: 3

YardenST
YardenST

Reputation: 5266

Dont add the choices directly to the model, add them to a form represnting the model later, by overriding the field with a set of choices.

than, do something like:

class MyForm(..):
    myfield_order_field = IntegerField(choices = [(i,i) for range(MyModel.objects.count)])
    class Meta():
        model = MyModel

if you want to use it in the admin, add to your Admin Class:

class MyModelAdmin(admin.ModelAdmin):
...

    form = MyForm

it will override this field in the admin too.

Upvotes: 1

Related Questions