How to add the OneToOneField in to the BD in Admin site that was created using setattr function

I have some classes in models.py, and after adding one more I need to add one more attribute in to the already created class, I found in internet that the best way to do this is to use setattr() function, so I made all and.. It seems work, no bugs, it is showing in the Admin site as <django.db.models.fields.related.OneToOneField> , but there are no way to chose it when I trying to create or change this field, there aren't , is it a bug or I doing smth wrong? Here is my code:

class PlayerInstance(models.Model):
    #...
class Competition(models.Model):
    #...
setattr(PlayerInstance, 'choice_to_competition', models.OneToOneField(
    Competition,
    default = 'for_nothoing',
    on_delete=models.RESTRICT, 
    help_text='which competition is it taking part?',
))

And no, I didn't forget to add 'choice_to_competition' in to the list_display. Here 2 photos of Admin site: I made them, but I can't add them here, can't understand reason, but it is not really necessary.

I found in internet that the best way to do this is to use setattr() function, so I made all and.

No. For "ordinary" classes that might work. But Django's Model has some advanced meta-logic, meaning attributes not passed when constructing the class, might end up not in the model.

What you need is to use the .contribute_to_class(…) method, like:

models.OneToOneField(
    Competition,
    default='for_nothoing',
    on_delete=models.RESTRICT,
    help_text='which competition is it taking part?',
).contribute_to_class(PlayerInstance, 'choice_to_competition')

But, you don't need to do this trick to refer to a model not yet created. Indeed, for a ForeignKey [Django-doc], OneToOneField model field [Django-doc] and ManyToManyField model field [Django-doc], you can use a string literal to refer to a model that has not yet been defined, with:

class PlayerInstance(models.Model):
    choice_to_competition = models.OneToOneField(
        'app_name.Competition',
        default='for_nothoing',
        on_delete=models.RESTRICT,
        help_text='which competition is it taking part?',
    )
    # …


class Competition(models.Model):
    # …

If the model has the same app label, you can even drop the app_label:

class PlayerInstance(models.Model):
    choice_to_competition = models.OneToOneField(
        'Competition',
        default='for_nothoing',
        on_delete=models.RESTRICT,
        help_text='which competition is it taking part?',
    )
    # …


class Competition(models.Model):
    # …

Note: Models normally have no Instance suffix. An object from a class is always called an "instance", by naming the class …Instance, one gets the impression that the variable is an instance of a class.

Back to Top