1

I'm trying to combine this answer and this one, with a bit of for looping.

On creating a character, I want to add all possible skills with a value of 0 but I'm getting confused on how to follow the above answers.

I have this mixin:

class CrossCharacterMixin(models.Model):
    cross_character_types = models.Q(app_label='mage', model='mage')
    content_type = models.ForeignKey(ContentType, limit_choices_to=cross_character_types,
                                     null=True, blank=True)
    object_id = models.PositiveIntegerField(null=True)
    content_object = GenericForeignKey('content_type', 'object_id')

    class Meta:
        abstract = True

(eventually, the cross_character_types will be expanded)

And this model:

class CharacterSkillLink(Trait, CrossCharacterMixin):
    PRIORITY_CHOICES = (
        (1, 'Primary'), (2, 'Secondary'), (3, 'Tertiary')
    )
    skill = models.ForeignKey('SkillAbility')
    priority = models.PositiveSmallIntegerField(
        choices=PRIORITY_CHOICES, default=None)
    speciality = models.CharField(max_length=200, null=True, blank=True)

    def __str__(self):
        spec_string = " (" + self.speciality + ")" if self.speciality else ""
        return self.skill.skill.label + spec_string

What I've started writing is this, on the NWODCharacter model:

def save(self, *args, **kwargs):
    if not self.pk:
        character_skills_through = CharacterSkillLink.content_object.model

        CharacterSkillLink.objects.bulk_create([
            [character_skills_through(skill=SkillAbility(
                skill), content_object=self) for skill in SkillAbility.Skills]
        ])

    super(NWODCharacter, self).save(*args, **kwargs)

This doesn't work as I don't think I'm passing in the right objects.

Based on this answer though:

from django.db import models

class Users(models.Model):
    pass

class Sample(models.Model):
    users = models.ManyToManyField(Users)

Users().save()
Users().save()

# Access the through model directly
ThroughModel = Sample.users.through

users = Users.objects.filter(pk__in=[1,2])

sample_object = Sample()
sample_object.save()

ThroughModel.objects.bulk_create([
    ThroughModel(users_id=users[0].pk, sample_id=sample_object.pk),
    ThroughModel(users_id=users[1].pk, sample_id=sample_object.pk)
])

In this situation, what is my ThroughModel? Is it CharacterSkillLink.content_object.model ?

How do I do this in my scenario? I'm sorry if this is trivial, but I'm struggling to get my head round it.

Community
  • 1
  • 1
AncientSwordRage
  • 7,086
  • 19
  • 90
  • 173

1 Answers1

1

It looks to me like CharacterSkillLink itself is your through model in this case... it generically joins a content type to a SkillAbility

If you think about it, it also makes sense that if you're doing a bulk_create the objects that you pass in must be of the same model you're doing a bulk_create on.

So I think you want something like this:

def save(self, *args, **kwargs):
    initialise_skill_links = not self.pk
    super(NWODCharacter, self).save(*args, **kwargs)
    if initialise_skill_links:
        CharacterSkillLink.objects.bulk_create([
            CharacterSkillLink(
                skill=SkillAbility.objects.get_or_create(skill=skill)[0],
                content_object=self
            )
            for skill in SkillAbility.Skills
        ])

Note you had too many pairs of [] inside your bulk_create.

Also I think you should use SkillAbility.objects.get_or_create()... for a foreign key you need the related object to exist. Just doing SkillAbility() won't fetch it from the db if it already exists and won't save it to the db if it doesn't.

Anentropic
  • 32,188
  • 12
  • 99
  • 147
  • Literally just left work, and heading home. Will try to use this tomorrow. You're right btw, `CharacterSkillLink` is my through model...I don't know what I was thinking. – AncientSwordRage Feb 10 '15 at 17:48
  • This works fine on my admin page, but it wasn't displaying any skills in the drop down box (separate issue) for that inline. I removed the inlines to see if it'd create and save the skills regardless, but now it's giving me two errors. The first is at the top of the error page: `ValueError at /admin/characters/mage/add/` and `Cannot assign "(, False)": "CharacterSkillLink.skill" must be a "SkillAbility" instance.` and then, `Error in formatting: 'CharacterSkillLink' object has no attribute 'speciality'` setting `speciality = ""` and specifying has no effect. Help? – AncientSwordRage Feb 11 '15 at 08:52
  • I found the issue `get_or_create` returns a tuple, and skill is assigned this tutple, not a `SkillAbility` object. This seems like it should work (it doesn't error) but I don't think it is working. I've added a GenericRelation to it and when I save, and try to access `mage_instance.skills.all()` it returns and empty list. – AncientSwordRage Feb 11 '15 at 11:58
  • 1
    I've updated the answer with the edit you wanted to make, also added `[0]` to deal with the tuple issue – Anentropic Feb 11 '15 at 14:10
  • I'm just testing this works now...I don't think it does but when I'm looking at the db in a db viewer the tables for CharacterSkillLink aren't being updated properly, the object_id is blank. Not sure if that is expected/normal? – AncientSwordRage Feb 11 '15 at 14:14
  • 1
    yes the problem is there is no `object_id` able to be set yet, because we are executing this code in a `if not self.pk` block... will update the answer – Anentropic Feb 11 '15 at 14:26
  • Well...yeah that seems obvious now you say it. – AncientSwordRage Feb 11 '15 at 14:26
  • Thanks that looks good, but what if I have a subclass that also calls save? Will that conflict? I'm begining to see how this is working now. If need be we could continue in [chat](http://chat.stackoverflow.com/rooms/70715/room-for-pureferret-and-anentropic) – AncientSwordRage Feb 11 '15 at 14:38
  • when you call `super` in the overridden `save` method of the subclass it will run the code above, so the mechanism will work fine still, you just have to decide in the subclass which things you want to do before the super call and which things after – Anentropic Feb 11 '15 at 14:42
  • I have a similar 'initialise' specific to the subclass. I'm trying to figure out whether or not it would conflict. Thanks for all your efforts though :) – AncientSwordRage Feb 11 '15 at 14:46