I've seen test codes where custom resources are further subclassed.
But it was for the test sake, not for any real use.
Why would anyone want to subclass a resource for obvious reason(but apparently not to me)?
An example would suffice.
I just ran into a situation where I need to subclass a resource.
I have a django model that has several subclasses like so (I've simplified it a lot):
class Information(models.Model):
name = models.CharField)
class Question(Information):
question = models.CharField)
class Link(Information):
url = models.CharField()
I want an API for accessing all of the Information objects, but still want to be able to access all the subclass specific attributes for each subclass. In this case I'd want to have 3 resources:
class InformationResource(ModelResource):
class Meta:
queryset = Information.objects.all()
def dehydrate(self, bundle):
'''Add some extra meta data here'''
class QuestionResource(InformationResource):
class Meta:
queryset = Question.objects.all()
class LinkResource(InformationResource):
class Meta:
queryset = Link.objects.all()
This way I have an api for returning all Information objects (as well as the attributes that are common to all of them), but if I need to access subclass specific attributes I can access them through their subclass resource APIs. Furthermore, these subclassed Resources will have access to all of the methods defined for their superclass. For instance, they can override the superclass' dehydrate method to add more fields.
Related
I would like to allow the creation of a comment only to those models that are sub-classing a specific mixin.
For example, a Post model will have a reverse GenericRelation relation to a Comments model. The comments model is using a custom content types mechanism implemented on top of django's due to the fact that the project uses sharding between multiple databases. The reverse relationship from Post model to Comments is needed to be able to delete Comments when a Post is also deleted.
Putting a simple coding example of what I would like to achieve:
class Post(models.Model, HasCommentsMixin):
some_fields = ....
class HasCommentsMixin(models.Model):
has_comments = GenericRelation('comments.Comment')
class Meta:
abstract = True
What I would like would me a way to say inside a permission check of a Model: if class is subclass of HasCommentsMixin, allow the creation of a comment. So for the Post model, comments can be created. But if it's not a subclass the Mixin, comments should not be allowed.
I hope I have provided a description that makes sense. I cannot share real code due to product license and protection.
Thank you.
To achieve this, you can use the isinstance() function in combination with the issubclass() function in the permission check to check if the model is a subclass of the HasCommentsMixin.
class IsCommentAllowed(permissions.BasePermission):
def has_permission(self, request, view):
model = view.get_queryset().model
if isinstance(model, HasCommentsMixin) or issubclass(model, HasCommentsMixin):
return True
return False
I have a custom model defined as following, with get() method override
class CustomQuerySetManager(models.QuerySet):
def get(self, *args, **kwargs):
print('Using custom manager')
# some other logics here...
return super(CustomQuerySetManager, self).get(*args, **kwargs)
class CustomModel(models.Model):
objects = CustomQuerySetManager.as_manager()
class Meta:
abstract = True
Then I have two models defined as
class Company(CustomModel):
name = models.CharField(max_length=40)
class People(CustomModel):
company = models.ForeignKey(Company, on_delete=models.CASCADE)
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
If I use get() directly like People.objects.get(pk=1) then it works, "Using custom manager" gets printed, but if I try to get the foreign key info, django still uses the get() method from the default manager, nothing gets printed and the rest of logic defined won't get executed, for example
someone = People.objects.get(id=1) # prints Using custom manager, custom logic executed
company_name = someone.company.name # nothing gets printed, custom logic does not execute
Is the foreign key field in a model using a different manager even though the foreign key model is also using my custom model class? Is there a way to make my custom get() method work for all fields?
As django doc says
By default, Django uses an instance of the Model._base_manager manager
class when accessing related objects (i.e. choice.question), not the
_default_manager on the related object
See more here.
So you have to tell django model which manager to use as base manager, like this:
class CustomModel(models.Model):
objects = CustomQuerySetManager.as_manager()
class Meta:
#django will use your custom "objects" manager as base_manager
#or you may have different managers for base and default managers
#if you define two managers with different names
base_manager_name = 'objects'
abstract = True
But, please, pay attention that you do not filter away any results from base manager. Django doc says:
This manager is used to access objects that are related to from some
other model. In those situations, Django has to be able to see all the
objects for the model it is fetching, so that anything which is
referred to can be retrieved.
Therefore, do not override get_queryset() for this kind of managers.
See more here.
I am trying to create a very simple CRUD application using REST API.
So I create a very simple model, serializer and viewset for all these.
And then I noticed that I don't fully understand some basic principals about right use-cases for calling (for example create method for my model instance)
As I understand, django providers several approaches:
I can define my CRUD methods inside model class:
class Foo(models.Model):
...
def create(...):
foo = Foo()
foo.save()
I also can create instances using model serializers (seems there is no big difference, because the same save method from model instance is calling):
class FooSerializer(seializer.ModelSerilizer):
...
class Meta:
model = Foo
....
def create():
fs = self.Meta.model()
fs.save()
2b. I can use simple serializers:
class FooSerializer(serializers.Serializer):
def create(**validated_data):
return Foo(**validated_data)
Finally I can use perform_create, update and so on from viewset:
class FooView(ModelViewSet):
serializer = FooSerializer
def perform_create():
serializer.save()
...
Is there some patterns when one or another solution should be implemented?
Could you please provide some explanation with use cases?
THanks!
Lets go step by step on your points of creating/using create method:
You don't need to write a create() method inside model.
You don't need to write a create() method in model serializer, unless you want to handle additional keywords or override the create() method to change the default behavior(Reference).
In serializer.Serializer you can write a create() method if you want save an instance with that serializer. Useful when you are using this serializer with GenericAPIViews or Viewsets. Reference can be found in documentation.
By writing perform_create() method in Viewset, you are basically overriding the default perform_create() from the Viewset. You can integrate additional tasks inside that function when overriding it(example).
I'm pulling my hair out trying to solve this (hopefully) simple problem.
I am using Django-nonrel, and I am trying to setup a few models. I've simplified the code below:
class Application(models.Model)
name = "Application"
def get_name(self):
print(self.name)
class ExampleApp(Application)
name = "Example Application"
class Site(models.Model)
app = models.OneToOneField(Application, null=True)
id = models.CharField(max_length=1)
# other details not important
When I obtain an instance of Site, I would like to obtain an instance of the derived class (e.g. ExampleApp)
class MyView(View):
def get(self, request, *args, **kwargs):
# Presuppose that I have an object with id="a"
site = Site.objects.filter(id="a")
#<Application: Application>
app = site.app
#returns "Application", not "Example Application"
app.get_name()
How can I access the derived class?
(Ideally, I would like Application to be an abstract base class, but django does not allow relationships to abstract base classes. I would define fields inside Application, but django-nonrel does not support multi-table inheritance.)
This is not possible, without additional Django plugins. See Find object in child class from object in parent class in django, where the answer by lazerscience suggests using https://code.google.com/p/django-polymorphic-models/ to access this functionality.
What Furbeenator answered is correct, but I feel that my (self-)answer could be more valuable to anyone in my situation.
Django-nonrel includes a few nice apps, notable djangotoolbox. Included in djangotoolbox is a particular field, EmbeddedModelField. It appears to store a copy of a model as a blob in the 'container' model.
In my example:
from djangotoolbox.fields import EmbeddedModelField
class Site(models.Model)
app = EmbeddedModelField(null=True)
id = models.CharField(max_length=1)
# other details not important
The upside to this is that, in my circumstances, it also meant that I could make my Application class abstract. The downside is that the data is now duplicated, and I still cannot access the Site instance from the Application instance (but in my case, that was an acceptable tradeoff).
I want to get a derived modelclass instance from an instanciated base modelclass.
I have the following model class hierarchy:
class AlfrescoPFCModel(models.Model):
#some fields and methods
class Meta:
abstract = True
class Contenido(AlfrescoPFCModel):
#some fields and methods
class Proyecto(Contenido):
#some fields and methods
class ProyectoCalificado(Proyecto):
#some fields and methods
class ProyectoArchivado(ProyectoCalificado):
#some fields and methods
And I instantiate a Proyecto class in this way:
proyecto = proyecto_form.save(commit=False)
#do some stuff with some fields that dont appear on the form
proyecto.save
In another view I try to access the derived class ProyectoCalificado from the base class Proyecto previously instanciated and saved in the database doing:
pc = ProyectoCalificado.objects.get(pk=id)
and i get: ProyectoCalificado matching query does not exist.
I also tried:
p = get_object_or_404(Proyecto, id=id)
pc = p.proyectocalificado
but it get the same error in the second line.
Reading the documentation i should be allowed to do that:
#multi-table-inheritance
What i want to do is to incrementally complete the data associated to a Proyecto (project) following this workflow: Proyecto -> ProyectoCalificado -> ProyectoArchivado.
I have 3 different forms for each step. I need 3 different models because I need to save them in the database without filling all the mandatory fields at once.
Thanks!
Use Form Wizards (https://docs.djangoproject.com/en/dev/ref/contrib/formtools/form-wizard/).
UPDATE
If you can't use Form Wizards because of the situation you describe, then you should make the models fields blank or nullable at the database level and then only enforce field requirements on each individual form. Creating three levels of inheritance solely for the purpose of the single-time set of forms required to create it is absolutely the wrong approach. It only fragments your data across additional tables and makes querying more complicated with no long-term benefit.
So, for example. Set the model itself up as though nothing (or only the items in the first form) are required. Then, in your first form, only make the fields necessary for that particular stage required. You can do this easily by overriding the __init__:
class FirstForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(FirstForm, self).__init__(*args, **kwargs)
self.fields['some_required_field'].required = True
# rinse and repeat
Do the same in your second and third forms, again making on the actual fields that are required for that particular form, required.
Then, call it day and have a drink.
If you have the child instance, it should have a <base class name>_ptr member which points to the instance of its superclass. You can use this as the basis of a filter query to retrieve the child.
You can also just assume that instances of the base and derived class will have the same id if you haven't done anything to affect how ids are allocated.