0

This question is similar to Set model field choices attribute at run time? However, my problem is that I want to change the default value of the choices attribute, at class level.

I have this

class Project(models.Model):

    name = models.CharField(...)

    @staticmethod
    def getAllProjectsTuple():
        return tuple([(p.id, p.name) for p in Project.objects.all()])

class Record(models.Model):

      project = models.ForeignKey(
                            Project,
                            verbose_name='Project',
                            help_text='Project name',
                            blank=True, null=True,
                            on_delete=models.SET_NULL,
                            choices = Project.getAllProjectsTuple(),

class RecordForm(ModelForm):

       class Meta:
              model = Record

My problem is that all the RecordForm will be created with a TypedChoiceField using the available choices that were in the model at the time of the module import. However, users can create more projects, and those will never appear in the RecordForm; same for deletion.

I tried tampering directly with the init of the RecordForm:

self.fields['project']._choices = [('', '---------'),] + [(p.id, p.name) for p in Project.objects.all()]

or with the init of the Record:

self._meta.get_field('project')._choices = Project.getAllProjectsTuple()

but nothing seems to work.

Thanks for your help.

Community
  • 1
  • 1
Ptah
  • 571
  • 1
  • 5
  • 8
  • 1
    Why are you using choices at all instead of just using the standard `ModelSelectWidget` with a queryset? The default behavior is the same as your `getAllProjectsTuple` behavior. – Peter DeGlopper Nov 22 '13 at 23:18
  • 1
    How about just `choices = Project.objects.values_list('id', 'name')` ? – karthikr Nov 22 '13 at 23:26
  • @Peter, this actually solved it. I am not sure I understand how. Is that some Django magic that will reevaluate the choices all the time if I don't override it? Thank you ! – Ptah Nov 22 '13 at 23:36
  • @Karthikr, That does not work. I think for the same reason as the original issue: This is evaluated only once at the model's import. – Ptah Nov 22 '13 at 23:37
  • @Ptah - it's not all that magical, really. The widget checks the database at the time you instantiate the form. Explicitly specifying `choices` is mostly for when you need to cover options that do not come from the database. – Peter DeGlopper Nov 23 '13 at 04:13
  • @PeterDeGlopper, OK, but what if I wanted to have, say the project_coordinator in my select instead of the name of the Project ? Should I use the solution I put below ? Is there a way to do it from the Model itself ? This is out of curiosity :-) – Ptah Nov 26 '13 at 20:53
  • 1
    To change that, you can either take the simple but wideranging option of changing the `__unicode__` method of `Project`, or the more complicated option of subclassing the field and overriding its `label_from_instance` method as documented here: https://docs.djangoproject.com/en/dev/ref/forms/fields/#modelchoicefield - the latter only affects the field, not other places where Django might represent your `Project` instances. – Peter DeGlopper Nov 26 '13 at 21:18
  • @karthikr After trying to do this with meta and whatnot, your simple solution worked for me from within the model . You might as well put your comment as an answer for future visitors. – bhaskarc Nov 19 '16 at 09:48

1 Answers1

0

I just figured out that:

self.fields['project'].choices = [('', '----------')] + [(p.id, p.name) for p in Project.objects.all()]

In the init of the RecordForm also does the trick. But @Peter DeGlopper answer in the comments is better.

Ptah
  • 571
  • 1
  • 5
  • 8