Parametrize filtering manager queryset in Django - django

I want to implement kind of row level security for my model in Django. I want to filter out data as low as it's possible based on some requirement.
Now, I know I could create specified managers for this model as docs says but it seems for me that it needs to be static. I also know that I can create just method that will return queryset as I want but I'll be not sufficient, I mean the possibility to just get all data is still there and the simplest mistake can lead to leak of them.
So I found this post but as author said - it's not safe nor pretty to mess around with global states. This post is nearly 10 years old so I hope that maybe someone has come up with a better generic solution.
Here is piece of example to visualise what I need:
models.py:
class A(models.Model):
...
class B(models.Model):
user = models.ForeignKey(User)
a = models.ForeignKey(A)
And I want to create global functionality for getting objects of A only if instance of B with user as logged in user exists.
I came up with solution to just override get_queryset() of A manager like so:
managers.py
class AManager(models.Manager):
def get_queryset(self):
return super().get_queryset(b__user=**and_here_i_need_a_user**)
but I can't find hot to parametrize it.
==== EDIT ====
Another idea is to simply not allow to get querysets of A explicitly but only via related field from B but I can't find any reference how to accomplish that. Has anyone done something like that?

So you're sort of on the right track. How about something like this...
class AQuerySet(models.QuerySet):
def filter_by_user(self, user, *args, **kwargs):
user_filter = Q(b__user=user)
return self.filter(user_filter, *args, **kwargs)
class AManager(models.Manager):
queryset_class = AQuerySet
def filter_by_user(self, user, *args, **kwargs):
return self.get_queryset().filter_by_user(user, *args, **kwargs)
class A(models.Model):
objects = AManager()
# ...
then you can use it like this:
A.objects.filter_by_user(get_current_user(), some_filter='some_value')

Related

Django + Factory Boy: Use Trait to create other factory objects

Is it possible to use Traits (or anything else in Factory Boy) to trigger the creation of other factory objects? For example: In a User-Purchase-Product situation, I want to create a user and inform that this user has a product purchased with something simple like that:
UserFactory.create(with_purchased_product=True)
Because it feels like too much trouble to call UserFactory, ProductFactory and PurchaseFactory, then crate the relationship between them. There has to be a simpler way to do that.
Any help would be appreciated.
First, I'll be honest with you: I do not know if this is the best answer or if it follows the good practices of python.
Anyway, the solution that I found for this kind of scenario was to use post_generation.
import factory
class UserFactory(factory.DjangoModelFactory):
class Meta:
model = User
name = factory.Faker('name'))
#factory.post_generation
def with_purchased_products(self, create, extracted, **kwargs):
if extracted is not None:
PurchaseFactory.create(user=self, with_products=extracted)
class PurchaseFactory(factory.DjangoModelFactory):
class Meta:
model = Purchase
user = factory.SubFactory(UserFactory)
#factory.post_generation
def with_products(self, create, extracted, **kwargs):
if extracted is not None:
ProductFactory.create_batch(extracted, purchase=self)
class ProductFactory(factory.DjangoModelFactory):
class Meta:
model = Product
purchase = factory.SubFactory(PurchaseFactory)
To make this work you just need to:
UserFactory.create(with_purchased_products=10)
And this is just a article that is helping learn more about Django tests with fakes & factories. Maybe can help you too.

Should default model fields be set by the Form or the Model?

Which option is best, 1 or 2?
1.
class TopicForm(forms.Form):
name = forms.CharField(required=True)
body = RichTextFormField(required=True)
def save(self, request):
t = models.Topic(user=request.user,
site=get_current_site(request),
name=self.cleaned_data['name'],
body=self.cleaned_data['body'])
t.slug = slugify(self.name)
t.body_html = seo.nofollow(seo.noindex(self.body))
t.ip = utils.get_client_ip(request)
t.save()
or 2.
class Topic(models.Model):
...
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
self.body_html = seo.nofollow(seo.noindex(self.body))
self.ip = utils.get_client_ip(request)
super(Topic, self).save(*args, **kwargs)
The difference is that the first version is only applied when modifying objects through the form, while the second is applied whenever the model is saved (though that is still a subset of all the ways in which database rows can be modified in Django). Even if you currently only create objects through forms, I think it's still a useful distinction to keep in mind.
It looks to me like a mixture of the two makes sense in your case. A slug is something that you will always want to set based on name - that is, it's inherent to the model itself. On the other hand, the idea of a client_ip seems inexorably tied to the notion of creating an object with a form via a web request.
Of course, you are in a better position to know about the specifics of this model, but that is the general way I would approach the question.
It depends. If this should be applied to every models, then it is better in the model. It will assure you that every Topic object will have correct values, even those you are edited from the admin interface.
The form should be use only to check data from the user and the model is appropriate to automatize this kind of task (generate data before saving the object). Be careful, this shouldn't raise Exception or invalidate data however.
Personally I would prefer the second option. The model should define the business logic too, while forms should just handle user I/O. This way your application will keep consistent even if used in a programmatic way (imported and called from other code).
You shouldnt use 2. its better to use a signal like pre-save or post-save
Source: https://docs.djangoproject.com/en/dev/topics/signals/
#receiver(pre_save, sender=Topic)
def topic_pre_save_handler(sender, instance, **kwargs):
instance.slug = slugify(self.name)
instance.body_html = seo.nofollow(seo.noindex(self.body))
instance.ip = utils.get_client_ip(request)

Best practice for static page elements in django

I have some page elements that don't change often and are displayed on every page like some adbars, footer content and such.
I want to change settings for this elements in my admin interface, so I have models for them.
Is there a best practice in django to deal with these elements?
Not really, no. You're describing a singleton pattern, so you might want to implement a singleton model type:
class SingletonModel(models.Model):
class Meta:
abstract = True
def save(self, *args, **kwargs):
self.id = 1
super(SingletonModel, self).save(*args, **kwargs)
def delete(self, *args, **kwargs):
pass
That will ensure that any model that inherits from that class can only ever have one member and that it can't be deleted. Other than that, I would suggest combining everything into just one model called something like SiteSettings with fields for header, footer, etc, instead of separate model for each.
You could use a context processor to add them to the context and use a simple caching mechanism so you don't have to hit the db every time like, http://eflorenzano.com/blog/2008/11/28/drop-dead-simple-django-caching/
Hard to answer - what exactly are you asking?
You can display these models in your base template. You can use caching to cut down on database calls.

django not taking in consideration model fields declared in __init__

When using Model class like this:
class MyModel(models.Model):
def __init__(self, *args, **kwargs):
self.myfield = models.Field()
super(MyModel, self).__init__(*args, **kwargs)
It doesn't take into consideration myfield(in the admin form, when saving the object... )
But if i declare like that:
class MyModel(models.Model):
myfield = models.Field()
It works just fine.
Why?
Edit
I think i have a good reason: I have an abstract class UploadItem that defines a field called file like this: self.file = models.FileField(upload_to=upload_to) As you can see, in each child class, i have to call parent init method with appropriate upload_to variable(say 'videos' for Video model). So i cannot do it the normal way.
Because the Django ORM code does some serious meta-magic during class definition (just browse the django/db code to see how magic). You are doing an end-run around that magic by creating fields on the fly in the __init__() function.
Is there a really good reason for not creating the class in the normal way? If not, then do it the normal way. If you do have a good reason then get ready to get into the really deep end of the pool -- both of Python and Django.
Setting a dynamic path for the upload_to attribute is absolutely not a good reason for wanting to muck around with model field declaration.
This is something that Django handles already - if you set upload_to to a callable, you can return the correct value dependent on the model instance. See the documentation.

Loose coupling of apps & model inheritance

I have a design question concerning Django. I am not quite sure how to apply the principle of loose coupling of apps to this specific problem:
I have an order-app that manages orders (in an online shop). Within this order-app I have two classes:
class Order(models.Model):
# some fields
def order_payment_complete(self):
# do something when payment complete, ie. ship products
pass
class Payment(models.Model):
order = models.ForeignKey(Order)
# some more fields
def save(self):
# determine if payment has been updated to status 'PAID'
if is_paid:
self.order.order_payment_complete()
super(Payment, self).save()
Now the actual problem: I have a more specialized app that kind of extends this order. So it adds some more fields to it, etc. Example:
class SpecializedOrder(Order):
# some more fields
def order_payment_complete(self):
# here we do some specific stuff
pass
Now of course the intended behaviour would be as follows: I create a SpecializedOrder, the payment for this order is placed and the order_payment_complete() method of the SpecializedOrder is called. However, since Payment is linked to Order, not SpecializedOrder, the order_payment_complete() method of the base Order is called.
I don't really know the best way to implement such a design. Maybe I am completely off - but I wanted to build this order-app so that I can use it for multiple purposes and wanted to keep it as generic as possible.
It would be great if someone could help me out here!
Thanks,
Nino
I think what you're looking for is the GenericForeignKey from the ContentTypes framework, which is shipped with Django in the contrib package. It handles recording the type and id of the subclass instance, and provides a seamless way to access the subclasses as a foreign key property on the model.
In your case, it would look something like this:
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class Payment(models.Model):
order_content_type = models.ForeignKey(ContentType)
order_object_id = models.PositiveIntegerField()
order = generic.GenericForeignKey('order_content_type', 'order_object_id')
You don't need to do anything special in order to use this foreign key... the generics handle setting and saving the order_content_type and order_object_id fields transparently:
s = SpecializedOrder()
p = Payment()
p.order = s
p.save()
Now, when your Payment save method runs:
if is_paid:
self.order.order_payment_complete() # self.order will be SpecializedOrder
The thing you want is called dynamic polymorphism and Django is really bad at it. (I can feel your pain)
The simplest solution I've seen so far is something like this:
1) Create a base class for all your models that need this kind of feature. Something like this: (code blatantly stolen from here)
class RelatedBase(models.Model):
childclassname = models.CharField(max_length=20, editable=False)
def save(self, *args, **kwargs):
if not self.childclassname:
self.childclassname = self.__class__.__name__.lower()
super(RelatedBase, self).save(*args, **kwargs)
#property
def rel_obj(self):
return getattr(self, self.childclassname)
class Meta:
abstract = True
2) Inherit your order from this class.
3) Whenever you need an Order object, use its rel_obj attribute, which will return you the underlying object.
This solution is far from being elegant, but I've yet to find a better one...