1

I have this model:

class Server(models.Model):
  user = models.ForeignKey('auth.User',related_name="servers")
  is_shared = models.BooleanField(default=True, editable=False)
  name = models.CharField(max_length=255, default='testserver')
  hostname = models.CharField(max_length=255, default='localhost')
  username = models.CharField(max_length=255, default='user')
  password = models.CharField(max_length=255, default='pass')
  path = models.CharField(max_length=255, default='/')
  mkdir = models.BooleanField(default=False)
  port = models.IntegerField(max_length=4, default=21)
  quirky = models.BooleanField(default=False)
  class Meta:
    unique_together = ('user', 'name',)
  def __unicode__(self):
    return self.name
  class Meta:
    verbose_name_plural = "Server"

And I'm creating a model form from that:

class ServerForm(ModelForm):
  delete = forms.BooleanField(required=False)

  def __init__(self, *args, **kwargs):
    super(ServerForm, self).__init__(*args,**kwargs)
    if (self.instance.pk is None):
        self.fields['name'] = forms.CharField(max_length=255, required=True)
        del self.fields['delete']
  class Meta:
    model = Server
    exclude = ["user","name"]

The point is that a user may not change the name field of an existing server object, but if there is no instance to the form I want the user to specify a name. In other words: The user is supposed to supply a name, but don't change it later.

When I use this code the name ends up at the bottom, but it should be on top. In earlier django version an insert could do the trick, but that doesn't work anymore since going to django 1.7.

Then I read here: How can I order fields in Django ModelForm?

... to use the fields value in the meta class. But now I have the problem that the field list should sometimes include "name" and other times not. Depending on whether there is an instance. But from the Meta class I cannot use self.instance to check. Also it seems a bit annoying to list all fields again just to change there order.

How can I change the order of my model form fields?

EDIT: I'd love to keep looping in the template. Also It would be nice to have the correct order in admin as well as any templates. Templates are more important though.

Community
  • 1
  • 1
JasonTS
  • 2,479
  • 4
  • 32
  • 48
  • Are you talking about ordering them in Django admin? or in the output of a form on the public side? – Brandon Taylor Oct 01 '14 at 14:25
  • I'm talking about the public side. It would be nice to have it in admin too though. I'm guessing your solution is to not loop in the template? I was hoping to avoid that.. – JasonTS Oct 01 '14 at 14:27
  • Bear with me a few minutes, I'm getting together a sample for you. – Brandon Taylor Oct 01 '14 at 14:37
  • Thank you. I'll stick around. Currently I'm trying to implement your suggestion and then I'll just try to put a formset using the update form and one extra form to add new stuff. – JasonTS Oct 01 '14 at 14:39
  • Grr. Well, unfortunately, I hit a brick wall with my example. It's possible to create a function that would return a meta class, like Media on a ModelForm, where you could access self, but unfortunately I can't seem to do the same with just the `Meta` class of a ModelForm. – Brandon Taylor Oct 01 '14 at 15:08
  • Well I solved it now in a way where I just pass a server update formset and an extra server add form to the template. It's not as pretty, but it does the trick. The worst thing is that I have to add validation code to the view as well. – JasonTS Oct 01 '14 at 15:46

2 Answers2

1

I might take advantage of inheritance and do something like this:

class ServerForm(ModelForm):

  def __init__(self, *args, **kwargs):
    super(ServerForm, self).__init__(*args,**kwargs)
    self.fields['name'] = forms.CharField(max_length=255, required=True)

    class Meta:
      model = Server
      exclude = ["user","name"]


class ServerUpdateForm(ServerForm):
  delete = forms.BooleanField(required=False)

  def __init__(self, *args, **kwargs):
    super(ServerForm, self).__init__(*args,**kwargs)

  class Meta:
    model = Server
    exclude = ["user","name"]
scoopseven
  • 1,767
  • 3
  • 18
  • 27
  • I haven't thought of that. But can I add this new form to the formset as well? I thought it has to be the same form over and over again. Every time I tried to have different forms in one formset it failed. – JasonTS Oct 01 '14 at 14:30
  • You should be able to do ServerFormSet = inlineformset_factory(ServerForm, ServerUpdateForm), but I guess that depends on your particular conditions for displaying the separate forms. – scoopseven Oct 01 '14 at 14:41
  • The documentation for "inlineformset_factory" seems a bit thin, but I'll give it a try. – JasonTS Oct 01 '14 at 14:44
  • I think "inlineformset_factory" is not going to work. As I understand it expects two related models that are connected through a foreign key. Is there anything else to combine different forms in a single formset? – JasonTS Oct 01 '14 at 14:55
0

Old question, but I did this very easily via jQuery. It was faster than messing with Meta or typing out the fields for every form (especially ones that don't inherit well). I use this for my Admin pages:

$("selector_for_field").insertBefore($(".form-row:first"));

Adjust selectors as needed for your form layout.

Supra621
  • 146
  • 3
  • 12