-2

I want to generate several related Model objects at once that are linked in the model view via stacked.Inline. I can create the objects in the admin view. However, when I go to the list view of the pipeline model I get:

'Pipeline' object has no attribute 'args'

I have pretty much the same setup working with other models, so I am not sure why it is not working in this case. It complains that 'Pipeline' has no args

model.py:

class Pipeline(models.Model):
    config= models.OneToOneField('Config', on_delete=models.SET_NULL, null=True, parent_link=True)


class Config(models.Model):
    args = models.CharField(max_length=256, null=True, default='-p -q -x -u -l -m -r')        
    pipeline = models.OneToOneField('Pipeline', on_delete=models.CASCADE, null=True, parent_link=False)

admin.py:

class ConfigInline(admin.StackedInline):
    model = Config


class PipelineAdmin(admin.ModelAdmin):
    inlines = [ConfigInline]

I did the database migrations.

Soerendip
  • 7,684
  • 15
  • 61
  • 128

2 Answers2

1

You set parent_link=True in your one to one field. According to the documentation for it:

When True and used in a model which inherits from another concrete model, indicates that this field should be used as the link back to the parent class, rather than the extra OneToOneField which would normally be implicitly created by subclassing.

You obviously don't use this while subclassing another model (This is called multi-table inheritance) hence that doesn't make sense. Change your implementation to:

class Pipeline(models.Model):
    config= models.OneToOneField('Config', on_delete=models.SET_NULL, null=True)


class Config(models.Model):
    args = models.CharField(max_length=256, null=True, default='-p -q -x -u -l -m -r')        
    pipeline = models.OneToOneField('Pipeline', on_delete=models.CASCADE, null=True)

Furthermore it still doesn't make sense as this way you basically have a foreign in both the tables which I believe is not what you want, keep the relation in only one model (whichever table the foreign key would be better suited to be in according to you):

class Pipeline(models.Model):
    pass


class Config(models.Model):
    args = models.CharField(max_length=256, null=True, default='-p -q -x -u -l -m -r')        
    pipeline = models.OneToOneField('Pipeline', on_delete=models.CASCADE, null=True)

# from config to get pipeline:
# related_pipeline = config.pipeline
# from pipeline to get config (Django will automatically add this related accessor):
# config = pipeline.config
Abdul Aziz Barkat
  • 19,475
  • 3
  • 20
  • 33
  • I totally misunderstood the purpose of `parent_link`, thanks for pointing that out. Now, everything works. However, now I have no reference to the config in the pipeline. – Soerendip Apr 20 '21 at 17:46
  • @Sören as I said Django automatically constructs a reference to the related models since this is a one to one field it will be the model name in lower case. So `pipeline.config` will give you the config. Alternatively if you want some other name you can set `related_name` so if you write `pipeline = models.OneToOneField('Pipeline', on_delete=models.CASCADE, null=True, related_name='other_name_for_config')` then `pipeline.other_name_for_config` will give you the related object. – Abdul Aziz Barkat Apr 20 '21 at 17:52
  • But I don't seem to be able to create both instances with the admin view in one go that way. It prompts me to add the config, but the config needs a pipeline, which has not been created yet. – Soerendip Apr 20 '21 at 18:04
  • From the django manual: "Specifying the parent link field¶ As mentioned, Django will automatically create a OneToOneField linking your child class back to any non-abstract parent models. If you want to control the name of the attribute linking back to the parent, you can create your own OneToOneField and set parent_link=True to indicate that your field is the link back to the parent class." – Soerendip Apr 20 '21 at 18:21
  • @Sören that is for [multi-table inheritance](https://docs.djangoproject.com/en/3.2/topics/db/models/#multi-table-inheritance) as I mention in the question. Regardless as I once again mention a related accessor is automatically made for each related field (one to one, foreign key, many to many each have some form of related accessor of their own) on the reverse side. – Abdul Aziz Barkat Apr 20 '21 at 18:24
  • At least with `parent_link=True` it finally works. And it is also stated here to used it like this: https://docs.djangoproject.com/en/dev/topics/db/models/#specifying-the-parent-link-field. Thank you, you helped me a lot! – Soerendip Apr 20 '21 at 18:44
  • I think you are mistaken. The manual explains that a OneToOne field is used for model inheritance, but it further down explains that OneToOne fields can be used as any other field. Furthermore, if the model attribute should have a specific name `parent_link` can be used to control it. Look under `OneToOne` field, not under `Model Inheritance`. – Soerendip Apr 22 '21 at 22:39
  • @Sören I am not mistaken. You are mistaken about the usage of `parent_link`. I know that `OneToOneField` can be used without model inheritance which can be seen from the point that in my answer `Config` does have a one to one field with `Pipeline` without inheriting from it. The fact is that `parent_link` is **not meant to be used without model inheritance**, I forgot to link the documentation when I quoted it, you can check again and see. – Abdul Aziz Barkat Apr 23 '21 at 04:36
  • @Sören as for the usage of `parent_link` it is there so you can make your own one to one field instead of using the implicitly made one, which in your comment you say "_Furthermore, if the model attribute should have a specific name `parent_link` can be used to control it_" here this is because we can now provide our own `related_name` instead of using the default `_ptr`. – Abdul Aziz Barkat Apr 23 '21 at 04:40
  • Hm, thanks for the clarification. I will try using inheritance, but I expect to run into cyclic import issues. – Soerendip Apr 23 '21 at 15:13
0

According to the conversation with Abdul the OneToOne field should only be used together with model inheritance. The following works, but it is not officially supported. I find the documentation somewhat ambiguous in that regard. So, consider this a hack:

class Pipeline(models.Model):
    pipeline = models.OneToOneField(
         'Pipeline', on_delete=models.CASCADE, null=True )

class Config(models.Model):
    args = models.CharField(
        max_length=256, null=True, default='-p -q -x -u -l -m -r' )        
    pipeline = models.OneToOneField(
        'Pipeline', on_delete=models.CASCADE, null=True, parent_link=True )

So, using the parent link was missing. Otherwise, in the admin pannel, both models cannot be created together.

According to this: "Specifying the parent link field As mentioned, Django will automatically create a OneToOneField linking your child class back to any non-abstract parent models. If you want to control the name of the attribute linking back to the parent, you can create your own OneToOneField and set parent_link=True to indicate that your field is the link back to the parent class." from

https://docs.djangoproject.com/en/dev/topics/db/models/#specifying-the-parent-link-field

Django Admin: OneToOne Relation as an Inline?

Soerendip
  • 7,684
  • 15
  • 61
  • 128