0

Consider the following code:

class MyManyToManyField(models.ManyToManyField):
  def __init__(self,*args,**kwargs):
    field_name = ?!?
    kwargs["related_name"] = "%(app_label)s.%(class)s." + field_name
    super(MetadataManyToManyField,self).__init__(*args,**kwargs)

class MyModelA(models.Model):
  modelAField = MyManyToManyField("MyModelB")

class MyModelB(models.Model):
  pass

Is there any way for me to access the name of the field from within my overloaded init function? I want the related_name of modelAField to wind up being "MyAppName.MyModelA.modelAField".

I've thought about just passing it as a kwarg:

  modelAField = MyManyToManyField("MyModelB",related_name="modelAField")

and then using it in init:

  field_name = kwargs.pop("related_name",None)
  if not field_name:
    raise AttributeError("you have to supply a related name!")

But I'm hoping for something a bit nicer.

Thanks.

trubliphone
  • 4,132
  • 3
  • 42
  • 66

3 Answers3

2

Use contribute_to_class

As has been mentioned before, a field object has to be instantiated, by calling its __init__ function, before it can be assigned to a variable within the class.

However, while the model is being constructed, Django looks for a method on the field called contribute_to_class. If it is present, it will be called like this:

new_field.object.contribute_to_class(ModelClass, field_name)

If you override this method in your custom field class, you can perform whatever initialisation is required at the point where the field is added to the model.

Ian Clelland
  • 43,011
  • 8
  • 86
  • 87
  • That is just a can of awesome. Love it! – Yuji 'Tomita' Tomita Feb 09 '12 at 23:17
  • This looks great, but if contribute_to_class gets called _before_ __init__(), then won't any attributes I set in it still be unavailable? I tried setting self._fieldName = the field_name argument passed into contribute_to_class. But when I access self._fieldName from __init__() it consistently gives the default value and not what I set it to in contribute_to_class. – trubliphone Feb 10 '12 at 06:31
  • Nearly one year later, I'm rewriting this application, and I ran into the same problem. It turns out that Ian was correct, I just had to modify the "related_name" argument in contribute_to_class by doing something like this: `self.rel.related_name = "some_string" + name` – trubliphone Jan 24 '13 at 03:43
1

I don't think this is possible. The object MyManyToManyField is going to be instantiated before it's assigned to a variable, so it's too early to be able to do anything clever.

There might be same magic in Django's metaclass for models which interacts with the fields, it might be possible to hijack this interaction and put you logic there. But I'm just speculating at this point.

Andrew Ingram
  • 5,160
  • 2
  • 25
  • 37
  • Yeah, that's what I figured. I have a working solution now, it's just bugging me b/c it's not very elegant. Thanks. – trubliphone Feb 09 '12 at 22:41
1

Passing as a kwarg doesn't seem that cumbersome.

I suspect that Python can't decide what the name of an instance is, since an object can have many reference pointers.

Eg

x = YourClass()
y = x

Is the name of the instance of YourClass 'x' or 'y'? Or is it just an instance of YourClass?

powlo
  • 2,538
  • 3
  • 28
  • 38
  • I take your point. But I thought there might be some DjangoForm specific attribute that I could access. After all, I know what the names of the fields are within the form class. – trubliphone Feb 09 '12 at 23:17