0

I want to be able to relate my User model, profile model, and a seperate model together. My current model structure looks like this:

class Profile(models.Model):
    COORDINATOR = 1
    LEADER = 2
    ADMIN = 3
    ROLE_CHOICES = (
        (COORDINATOR, 'Coordinator'),
        (LEADER, 'Leader'),
        (ADMIN, 'Admin'),
    )
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    team = models.ForeignKey(Team, on_delete=models.PROTECT,null=True)
    role = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, null=True, blank=True)
    agent_code = models.CharField(max_length=15, null=True, blank=True)

class DailyReports(models.Model):
    agent_code = models.CharField(max_length=15, blank=True, null=True)
    product = models.CharField(max_length=15)
    num_free = models.IntegerField(blank=True, null=True)
    apps_submitted = models.IntegerField(blank=True, null=True)
    apps_activated = models.IntegerField(blank=True, null=True)
    prem_submitted = models.DecimalField(max_digits=20, decimal_places=2,blank=True, null=True)
    date = models.DateField(auto_now=False,auto_now_add=False,null=True,blank=True)

I can relate Profile and User, but I'm attempting to relate Profile to DailyReports on agent_code, and then relate to the User model.

So with something like the following:

    test_query = DailyReports.objects.filter(product__in=['LT15', 'LT121']) \
            .values('agent_code') \
            .annotate(premium=Sum('prem_submitted')) \
            .order_by('-premium') \

I get the output as expected:

{'agent_code': 'ABC123', 'premium': Decimal('50015.87')} 
{'agent_code': 'DEF456', 'premium': Decimal('44818.20')} 
{'agent_code': 'GHI789', 'premium': Decimal('35322.35')}
...

But I also want to get the information from the Profile based on agent_code, and then the related User information based on the relation made between the agent_code between Profile and DailyReports, such that my output would look like:

{'agent_code': 'ABC123', 'premium': Decimal('479872.55'), user.profile.first_name, user.profile.last_name, profile.user_id, profile.role} 
{'agent_code': 'DEF456', 'premium': Decimal('448118.20'), user.profile.first_name, user.profile.last_name, profile.user_id, profile.role} 
{'agent_code': 'GHI789', 'premium': Decimal('356322.35'), user.profile.first_name, user.profile.last_name, profile.user_id, profile.role} 
Andrew
  • 12,172
  • 16
  • 46
  • 61
  • You need to use a GenericForeignKey https://docs.djangoproject.com/en/2.1/ref/contrib/contenttypes/#generic-relations – jackotonye Sep 03 '18 at 20:46
  • Thanks! Not exactly sure how to implement it, but I'll dig into the documentation and try to find some examples online. – Andrew Sep 03 '18 at 21:22
  • Do you want the relationship to be concrete since it seems agent_code is a char field you will need to ensure they are unique and can use `agent_code = models.ForeignKey(DailyReports, to_field='agent_code')` on the Profile model – jackotonye Sep 03 '18 at 21:32
  • They're unique to each `Profile`, and I guess a one-to-many relation to `DailyReports`, since there's multiple instances of `DailyReports` objects with the same `agent_code`. I'm lost on how to implement that. If I made the `Profile.agent_code` a PK and `DailyReports.agent_code` a ForeignKey, is that a route to go? Not sure how to implement GenericForeignKey() in my example. – Andrew Sep 03 '18 at 21:40
  • I also get an `import error` when attempting `from reports.models import DailyReports` in Profiles, but no error with `from reports.models import *`. Not sure what's going on. Edit: Narrowed that down to circular imports, I believe. – Andrew Sep 03 '18 at 22:54

1 Answers1

1

If one Profile will be linked to multiple DailyReports best setup will have to be

class Profile(models.Model):
    COORDINATOR = 1
    LEADER = 2
    ADMIN = 3
    ROLE_CHOICES = (
        (COORDINATOR, 'Coordinator'),
        (LEADER, 'Leader'),
        (ADMIN, 'Admin'),
    )
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    team = models.ForeignKey(Team, on_delete=models.PROTECT,null=True)
    role = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, null=True, blank=True)
    # agent_code = models.CharField(max_length=15, null=True, blank=True) # <-- Remove the agent code from the profile model.

The DailyReports with be a many to one relationship to the Profile.

class DailyReports(models.Model):
    profile = models.ForeignKey('Profile', related_name='daily_reports')
    agent_code = models.CharField(max_length=15, blank=True, null=True)
    product = models.CharField(max_length=15)
    num_free = models.IntegerField(blank=True, null=True)
    apps_submitted = models.IntegerField(blank=True, null=True)
    apps_activated = models.IntegerField(blank=True, null=True)
    prem_submitted = models.DecimalField(max_digits=20, decimal_places=2,blank=True, null=True)
    date = models.DateField(auto_now=False,auto_now_add=False,null=True,blank=True)

To get the list of DailyReports for a profile

profile = Profile.objects.prefetch_related('daily_reports').first()

profile.daily_reports.all()

To query by the profile reports

qs = (
    profile.daily_reports.filter(product__in=['LT15', 'LT121'])
        .annotate(premium=Sum('prem_submitted'))
        .values_list('agent_code', 'premium', 'profile__first_name', 'profile__last_name', 'profile__user_id', 'profile__role', named=True)
        .order_by('-premium')
)

results = [
    {
       'agent_code': report.agent_code,
       'premium': report.premium
       'first_name': report.profile__first_name,
       'last_name': report.profile__last_name,
       'user_id': report.profile__user_id,
       'role': report.profile__role
    } for report in qs
]

## {'agent_code': 'ABC123', 'premium': Decimal('479872.55'), ...} 
jackotonye
  • 3,537
  • 23
  • 31