1

I'm working with a legacy database where I have a serializer setup on Table A like so -

class TblapplicationsSerializer(serializers.ModelSerializer):

    class Meta:
        model = Tblapplications
        fields = ('applicationid', 'applicationname', 'description', 'drtierid', 'saglink', 'supportinstructions',
                  'defaultincidentpriorityid', 'applicationorigintypeid', 'installationtypeid', 'comments',
                  'lastmodifieddate', 'lastmodifiedby', 'assetstatusid', 'recordownerid', 'adl_app')
        depth = 2

I'm using a standard filter -

class TblapplicationsFilter(django_filters.FilterSet):
    name = django_filters.CharFilter(name="applicationname", lookup_type="exact")
    env = django_filters.CharFilter(name="adl_app__environmentid__domain")

    class Meta:
        model = Tblapplications
        fields = ['applicationname', 'name', 'env']

Here's where it goes sideways. What I want to be able to do is filter on my URL like /api/applications/?name=xxx&env=DEV. It would then return the application and any databases that are linked with the environment of DEV. The name was understandably easy, but the only was I figured out the environment was to make the api point for applications touch the middle table that links the two but it returns multiple values because it's grabbing each time application is referenced with a separate database.

I've updated the Serializer and Filter based on comments given and the serializer, without the &env=DEV returns all the appropriate data (domain is nested in a reverse relationship). I then want my filter to filter the results based on that. Which means that it needs to somehow know to limit the results on the reverse relationship to only what's provided from the nested value.

If you see my models -

class Tblapplicationdatabaselinks(models.Model):
    id = models.AutoField(db_column='ID', primary_key=True)
    applicationid = models.ForeignKey('Tblapplications', db_column='applicationId', to_field='applicationid',
                                      related_name='adl_app')
    dbid = models.ForeignKey('Tbldatabases', db_column='dbId', to_field='id', related_name='adl_db')
    environmentid = models.ForeignKey('Tbldomaincodes', db_column='environmentId', to_field='id',
                                      related_name='adl_envlink')
    comments = models.TextField(blank=True)
    lastmodifieddate = models.DateTimeField(db_column='lastModifiedDate', blank=True, null=True)
    lastmodifiedby = models.CharField(db_column='lastModifiedBy', max_length=255, blank=True)
#    upsize_ts = models.TextField(blank=True) # This field type is a guess.

    class Meta:
        managed = False
        db_table = 'tblApplicationDatabaseLinks'

class Tblapplications(models.Model):
    applicationid = models.AutoField(db_column='applicationId', primary_key=True)
    applicationname = models.CharField(db_column='applicationName', max_length=255)
    description = models.TextField(blank=True)
    drtierid = models.ForeignKey(Tbldomaincodes, db_column='drTierID', blank=True, null=True, to_field='id',
                                 related_name='app_drtier')
    saglink = models.TextField(db_column='sagLink', blank=True)
    supportinstructions = models.TextField(db_column='supportInstructions', blank=True)
    defaultincidentpriorityid = models.IntegerField(db_column='defaultIncidentPriorityId', blank=True, null=True)
    applicationorigintypeid = models.IntegerField(db_column='applicationOriginTypeId')
    installationtypeid = models.ForeignKey(Tbldomaincodes, db_column='installationTypeId', to_field='id',
                                           related_name='app_insttype')
    comments = models.TextField(blank=True)
    assetstatusid = models.ForeignKey(Tbldomaincodes, db_column='assetStatusId', to_field='id',
                                      related_name='app_status')
    recordownerid = models.ForeignKey(Tblusergroups, db_column='recordOwnerId', blank=True, null=True,
                                      to_field='groupid', related_name='app_owner')
    lastmodifieddate = models.DateTimeField(db_column='lastModifiedDate', blank=True, null=True)
    lastmodifiedby = models.CharField(db_column='lastModifiedBy', max_length=255, blank=True)
#    upsize_ts = models.TextField(blank=True) # This field type is a guess.

    class Meta:
        managed = False
        db_table = 'tblApplications'

class Tbldatabases(models.Model):
    dbid = models.AutoField(db_column='dbId', primary_key=True)
    dbname = models.CharField(db_column='dbName', max_length=255)
    serverid = models.ForeignKey('Tblservers', db_column='serverId', to_field='serverid', related_name='db_serv')
    servicename = models.CharField(db_column='serviceName', max_length=255, blank=True)
    dbtypeid = models.IntegerField(db_column='dbTypeId', blank=True, null=True)
    inceptiondate = models.DateTimeField(db_column='inceptionDate', blank=True, null=True)
    comments = models.TextField(blank=True)
    assetstatusid = models.IntegerField(db_column='assetStatusId')
    recordownerid = models.IntegerField(db_column='recordOwnerId', blank=True, null=True)
    lastmodifieddate = models.DateTimeField(db_column='lastModifiedDate', blank=True, null=True)
    lastmodifiedby = models.CharField(db_column='lastModifiedBy', max_length=255, blank=True)
#    upsize_ts = models.TextField(blank=True) # This field type is a guess.

    class Meta:
        managed = False
        db_table = 'tblDatabases'

class Tbldomaincodes(models.Model):
    id = models.IntegerField(db_column='ID', primary_key=True)
    domain = models.CharField(primary_key=True, max_length=255)
    displayname = models.CharField(db_column='displayName', primary_key=True, max_length=255)
    displayorder = models.IntegerField(db_column='displayOrder', blank=True, null=True)
    comments = models.TextField(blank=True)
    lastmodifieddate = models.DateTimeField(db_column='lastModifiedDate', blank=True, null=True)
    lastmodifiedby = models.CharField(db_column='lastModifiedBy', max_length=255, blank=True)
#    upsize_ts = models.TextField(blank=True) # This field type is a guess.

    class Meta:
        managed = False
        db_table = 'tblDomainCodes'
Moe Far
  • 2,742
  • 2
  • 23
  • 41
whoisearth
  • 4,080
  • 13
  • 62
  • 130

1 Answers1

1

Extend your filter set and reference the field in the other model:

class TblapplicationsFilter(django_filters.FilterSet):
    name = django_filters.CharFilter(name="applicationname", lookup_type="exact")
    env = django_filters.CharFilter(name="environmentid__name")
    #                                    ^^^^^^^^^^^^^^^^^^^

    class Meta:
        model = Tblapplications
        fields = ['applicationname', 'name', 'env']

Also, you may wish to name your ForeignKey fields without the id suffix, which is the Django convention. In Django, when you access Tblapplications.environmentid, it is normally a model instance, not the actual id integer itself.

Ross Rogers
  • 23,523
  • 27
  • 108
  • 164
  • Thnx. unfortunately I'm working with a legacy DB so not changing the field names. That said, I had tried this but unfortunately because the mapping is coming from a reverse relationship it doesn't work instead returning that it can't find the field `environmentid`. I change it to `adl_app__environmentid__name` which doesn't error but then it returns nothing. – whoisearth Nov 21 '15 at 19:42
  • 1
    To debug what is going on, log all SQL queries to the console with [this answer](http://stackoverflow.com/a/11922878/20712). You can debug the relationships interactively with `python manage.py shell`, importing your models, and then filtering while looking at the printed SQL to console like `foo_model.objects.filter(adl_app__environmentid__name='bar')` – Ross Rogers Nov 21 '15 at 20:40
  • 1
    As far as changing the names of legacy DB columns, this is only a Django visible thing. I work with a _horrific_ legacy DB whose names are the worst names I have ever seen. I renamed them with `new_name = models.CharField(db_column='crappy_name')` and I'm able to do reverse relationships. The crucial part is the `db_column` parameter. – Ross Rogers Nov 21 '15 at 20:44
  • 1
    Anyways, do the interactive shell with console logging and you'll be able to work out what the name of your `CharFilter`'s `name` parameter should be. – Ross Rogers Nov 21 '15 at 20:45
  • 1
    This is great thanks! definitely wasn't thinking about the renaming fields within django doing that now. as for the return realized looking at the db again it was `displayname` which had the environment. problem is now it's returning everything . I think I need to re-think this. – whoisearth Nov 21 '15 at 21:21
  • Good luck, whoisearth! – Ross Rogers Nov 22 '15 at 23:47