Retrieving the Model object using table name - django

I would like to retrieve the Model object while knowing the table_name
For ex:
class User(models.Model):
class Meta:
db_table = 'my_users_table'
Are there ways that return User by taking my_users_table as input?

I would like to retrieve the Model object
I think you mean the Model class here instead of object.
One possible solution that I can think of is to get all the models and match for db_table:
from django.apps import apps
model = next((m for m in apps.get_models() if m._meta.db_table=='my_users_table'), None)
If there is no model with the given db_table name then the model will be None.

I don't think there is a direct way to do this. But you can create your own function which will do this for you.
Based on https://stackoverflow.com/a/29739109/3627387
from django.apps import apps
def get_model_by_db_table(db_table):
for model in apps.get_models():
if model._meta.db_table == db_table:
return model
else:
# here you can do fallback logic if no model with db_table found
raise ValueError('No model found with db_table {}!'.format(db_table))
# or return None
Or you can do this using ContentType model
Edit:
The link provided for ContentType is broken. This may be tried instead.

Related

Serialize foreign key object that is AbstractUser

I am serializing a model using the Django REST framework successfully, but would like to add a field from a related model. I have seen other posts describe how to do this using nested serializers, however mine is different because the other model I am trying to access is an AbstractUser class.
I would like to serialize the UserDefinedEquipName field from CustomUser.
models (some fields removed for clarity):
accounts/models.py
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
UserDefinedEquipName = models.CharField(max_length=50, default = "Default equip",)
....
builds/models.py
from accounts.models import CustomUser
from django.contrib.auth import get_user_model
class Build(models.Model):
author = models.ForeignKey(get_user_model(),on_delete=models.CASCADE,)
machineName = models.OneToOneField(max_length=50,blank=True,)
....
So my thought is to pass the value into the serializer but can't seem to figure out how to access the value without getting error AttributeError: type object 'Build' has no attribute 'CustomUser'
I have tried:
My serializers.py:
from rest_framework import serializers
from .models import Data, Build, CustomUser
from django.contrib.auth.models import AbstractUser
class buildStatsAPI_serializer(serializers.ModelSerializer):
equipName = Build.CustomUser.UserDefinedEquipName
#also tried:
#equipName = Build.CustomUser__set.all()
class Meta:
fields = ('id','author','machineName','equipName',)
model = Build
Am I missing something small here? Or is there a much better way of doing this. It seems like if this wasn't an AbstractUser class it would be much easier.
EDIT - Added views.py
class buildStatsAPI(generics.ListCreateAPIView):#for build stats JSON
permission_classes = (permissions.IsAuthenticated,)
serializer_class = buildStatsAPI_serializer
def get_queryset(self):
machinesOwned =CustomUser.objects.filter(customerTag=self.request.user.customerTag).filter(isDevice=True)
machineList = []
for machine in machinesOwned:
machineList = machineList + [machine.id]
query = Build.objects.filter(deleted=0, author_id__in=machineList,).values().order_by('pk')
return query
I think you are defining the Serializer improperly. You can't directly reference Model in a serializer. You need to use any kind of fields. For example, if you use SerializerMethodField, you can try like this:
class buildStatsAPI_serializer(serializers.ModelSerializer):
equipName = serializers.SerializerMethodField()
class Meta:
fields = ('id','author','machineName','equipName',)
model = Build
def get_equipName(self, obj):
# here obj is a build model object
return obj.author.UserDefinedEquipName
Update
Please update your get_queryset method so that it returns a queryset like this(I have refactored it a bit):
def get_queryset(self):
query = Build.objects.filter(deleted=0, author__customerTag=self.request.user.customerTag, author__isDevice=True) # removed .values() because it will return a dictionary
return query

Django - Simplify Proxy Model to single class

I have User table in my DB, they can be active or inactive. If I only want to query on active user, I define a Proxy Model like following.
class User(models.Model):
name = models.CharField(max_length=50)
location = models.CharField(max_length=50)
active = models.BooleanField()
class UserActive(models.Manager):
def get_queryset(self):
return super(UserActive, self).get_queryset().filter(active=True)
class ActiveUser(User):
objects = UserActive()
class Meta:
proxy = True
Then by working with ActiveUser, I can do my calculation/statistic with only active user.
The problem is, I need to define both UserActive and ActiveUser class, it seems awkward to me. Because with each main class (in this case is User), we need to define two other classes. Imaging we have several other model need to implement Proxy, the code would look messy. May I know if we can have more elegant way ?
Thanks
Alex
I would really avoid overwriting the .objects manager, and use this as some sort of implicit filtering. The Zen of Python is explicit is better than implicit, by using ActiveUser, you basically implement a filtering manager, but propose it like the entire set.
Perhaps a more elegant solution is to define multiple managers. So we can construct a filtering manager decorator:
def filter_manager(**kwargs):
def decorator(klass):
def get_queryset(self):
return super(klass, self).get_queryset().filter(**kwargs)
klass.get_queryset = get_queryset
return klass
return decorator
This decorator will however throw away a get_queryset that is defined on the manager itself, so you can not perform an extra patch with this.
Now we can define some managers in a rather elegant way:
#filter_manager(active=True)
class ActiveManager(models.Manager):
pass
#filter_manager(active=False)
class InactiveManager(models.Manager):
pass
Finally we can add these managers to the User model, and use explicit names:
class User(models.Model):
name = models.CharField(max_length=50)
location = models.CharField(max_length=50)
active = models.BooleanField()
objects = models.Manager()
active_users = ActiveManager()
inactive_users = InactiveManager()
So now we can use User.active_users to query for the active users. We thus have no proxy models, and can query with User.active_users.count() for example (well we can perform all operations like with .objects but then for .active_users.
I created a new Django project, with only User model. My models.py look like this
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
def filter_manager(**kwargs):
def decorator(klass):
def get_queryset(self):
return super(klass, self).get_queryset().filter(**kwargs)
klass.get_queryset = get_queryset
return klass
return decorator
#filter_manager(active=True)
class ActiveManager(models.Manager):
pass
#filter_manager(active=False)
class InactiveManager(models.Manager):
pass
class User(models.Model):
name = models.CharField(max_length=50)
location = models.CharField(max_length=50)
active = models.BooleanField()
active_user = ActiveManager()
When I tried User.objects.all().
Error: type object 'User' has no attribute 'objects'

Using ModelSerializer with joined records

I am trying to make a tool for drawing diagrams on the web. I have a model like so:
class PlaneableItem(Model):
name = models.CharField(max_length=NAME_LENGTH, blank=True)
class View(PlaneableItem):
# Some useful details
class Anchor(Model):
view = models.ForeignKey(View)
planeable = models.ForeignKey(PlaneableItem)
class BlockRepresentation(Anchor):
# Useful details
class LineRepresentation(Anchor):
# Useful details
I try to make a rest API that returns lists of all blocks and lines for a specific view, including the name of the planeable that they refer to.
I can get a queryset for this using:
qs = BlockRepresentation.objects.filter(view=theview).all()
qs.select_related('planeable')
qs.extra(select={'name': 'rest_api_planeableitem.name'})
However, now I can't use a ModelSerializer on it, because the field 'name' is not part of the BlockRepresentation.
I really like ModelSerializers, is there a better way of doing this?
Is there a particular reason you need that extra() call? If the sole purpose of that call is to rename a field, you can omit that from the queryset and rename the field using a SerializerMethodField from your serializer. I will assume planeable is the ForeignKey field in BlockRepresentation model to the PlaneableItem model. Sample code:
from rest_framework import serializers
class BlockRepresentationSerializer(serializers.ModelSerializer):
# Some fields
name = serializers.SerializerMethodField()
class Meta:
model = BlockRepresentation
def get_name(self, obj):
if obj.planeable:
return obj.planeable.name
return ''

Django-import-export - export from model's functions?

For a Django model I'm using django-import-export package.
The manual says I can export fields that are not existing in the target model like so:
from import_export import fields
class BookResource(resources.ModelResource):
myfield = fields.Field(column_name='myfield')
class Meta:
model = Book
http://django-import-export.readthedocs.org/en/latest/getting_started.html
How do I export the output of functions from the model? e.g. Book.firstword()
Here's how you should do it (check this out https://django-import-export.readthedocs.org/en/latest/getting_started.html#advanced-data-manipulation):
from import_export import fields, resources
class BookResource(resources.ModelResource):
firstword = fields.Field()
def dehydrate_firstword(self, book):
return book.firstword()
class Meta:
model = Book
Update to answer OP comment
To return fields in a particular order, you can user the export_order Meta option (https://django-import-export.readthedocs.org/en/latest/api_resources.html?highlight=export_order#import_export.resources.ResourceOptions).
There is one more solution with less code, than that suggested by Serafeim:
from import_export import fields, resources
class BookResource(resources.ModelResource):
firstword = fields.Field(attribute='firstword')
class Meta:
model = Book
Just in case you need to get the full URL of the field and based on #Serafeim's solution
class CompanyModelResource(ModelResource):
def dehydrate_local_logo(self, company):
if company.local_logo and hasattr(company.local_logo, 'url'):
return company.local_logo.url
return company.local_logo
class Meta:
model = Company

Django filter query using hasattr (or something like it)

I want to perform a filter on a model and return all objects that have a specific attribute.
model.objects.filter(hasattr(model, 'attrname'))
This obviously doesn't work, but not sure how to efficiently implement something siilar.
Thanks
EDIT
An example of where I would use this is when a model is inherited from another
class model1(models.Model):
...
class model2(model1):
...
if I do a model1.objects.all() each of the returned objects that are in model2 will have an extra attribute
If the models are related, you can the isnull in the filter.
model1.objects.filter('related_name__field_name__isnull=False)
where related name is in for the foreign key in model2
For Example:
class Owner(models.Model):
user = models.CharField(max_length=10)
class Car(models.Model):
car_type = models.CharField(max_length=10)
owner = models.ForeignKey(Owner, related_name='cars',
on_delete=models.CASCADE)
For owners with cars:
owners = Owner.objects.filter(cars__id__isnull=False)
I just put it in a:
try:
....
except AttributeError:
....
The way I did it was to suppress the FieldError exception:
from django.core.exceptions import FieldError
from contextlib import suppress
with suppress(FieldError):
model.objects.filter(field_in_other_class=value)
hope that helps