Handling uniqueness in a multi-tenant Django setup - django

I ahve a multi tenant Django database. All my multi tenant enabled models import from a class AccountSpecificModel, which as a FK to a class Account. Now I have some unique=True and unqiue_together in Meta, which do not specify account.
In my scenario, for AccountSpecificModel uniqueness has no menaing unless it takes care of account, which means I want to convert each unique to a unique_together with account, and simlar for unique_together.
How can I do this?

If I'm understanding you correctly, this is what you want:
class Client(models.Model):
account = models.ForeignKey(Account)
email = models.EmailField()
class Meta:
unique_together = (('account', 'email'),)
Notice the "two-tuple" I assigned unique_together to. You can do a standard tuple, but if there is more than one field that you want to be unique together, you'll just have to change it anyway.
Now 500 different accounts can have a client with the email example#example.com, but no account can have 2 clients with the same email.

I did not understand your question fully, but I found that when implementing complicated django model class customizations one good solution is a class factory. I found that it is less complicated and less surprising than multiple inheritance and other magic.
def factory(superclass, arguments):
class SomeClass(superclass):
[...]
class Meta:
[...]
return SomeClass
RealClass = factory(SuperClass, args)
I tried other approaches on a similar sounding problem for a while, but a factory for the class solved the problem in the end. Think about it, it may help you to solve your problem.

Related

Prefetch queryset when related_name="+"

Is it possible without related name (related_name="+") to prefetch objects on the target instance? Sure I know it's not a problem with the related name, but I'm not really sure if it's possible without it.
Here is the example code:
from django.db import models
class Parent(models.Model):
name = models.CharField(max_length=50)
class Child(models.Model):
parent = models.ForeignKey(to=Parent, related_name="+", on_delete=models.CASCADE)
name = models.CharField(max_length=50)
Parent.objects.all().prefetch_related('child_set')
Maybe it's possible using the Prefetch(lookup, queryset=None, to_attr=None) object, because it takes the queryset in the argument list?
Looked through the code a bit and found this line:
rel_obj_descriptor = getattr(instance.__class__, through_attr, None)
Here instance is the model instance, and through_attr is the field name of related instance to be fetched. This line basically tries to get a related descriptor to perform the prefetch query. In your case rel_obj_descriptor would contain None.
To answer your question no it is not possible at least for a Foreign Key, there may be some hack for Many to Many relationships as Django appears to use some internal descriptors for them.
I would advice you to simply not set related_name="+" since you want to use the backwards relation here. You say "It's because of separation of concerns between multiple apps" but that does not make much sense. Don't we set a foreign key to the user model for various other models anyway and still use the related name? Does the point of separation of concerns arise there(the user model is in a separate app)?
try
parent = Parent.objects.get(id=pk)
parent.child_set.all()
I don't know if having related_name = '+' prevents this situation, but if you never define related_name, you can definitely use this method.

How can I use a different object manager for related models?

I have a couple of classes:
class Person(models.Model):
address = models.ForeignKey(Address, on_delete=models.CASCADE)
class Address(LiveModel):
address = models.CharField(max_length=512)
some_manager = SomeManager()
some_other_object_manager = OtherManager()
class Meta:
base_manager_name = 'some_other_object_manager'
Because I set some_manager, the default manager used is SomeManager which is good. BUT if I am querying a Person, I want it to use Address's OtherManager manager for querying and I thought that by setting base_manager_name, I would be able to achieve this (https://docs.djangoproject.com/en/2.2/topics/db/managers/#using-managers-for-related-object-access). Unfortunately this does not work. Any ideas? Particularly I am trying to achieve this in the admin, if that makes a difference.
To clarify, this does work as intended. The issue here was in the detail. I was using the Django admin which does not query the related fields the way I expected. It actually uses the related fields default manager for the queryset. If you want to do what I am trying to do, this is a nice simple example: https://books.agiliq.com/projects/django-admin-cookbook/en/latest/filter_fk_dropdown.html

AbstractClass ForeignKey reference to AbstractClass

I am building a compliance management system where I have the following requirements for most models in my project:
My Requirements
Users with certain roles can change fields, but this has to lead to a new "version" with a draft status
After approval through certain other roles (i.e. managers) this version of the model has to be published
History has to be accessible for all roles
Current Situation
Because available django-apps did not meet all these requirements I created to following constuct:
Every model (e.g. Policy) has one Master and one Detail model.
The Master model has the following fields:
id
deleted
currentActiveDetail (ForeignKey)
The Detail model has the following fields:
id
majorVersion
minorVersion
author
masterModel (ForeignKey to Master)
user (ForeignKey to auth.User)
lifecycleStatus (Choice, i.e. 'Draft', 'Waiting Approval', 'Approved', 'Obsolete')
a lot of content fields (e.g. Description, Text,...)
a lot of methods
Challenges/Questions
Because I have a lot of such use cases I want to make a MasterAbstractClass and a DetailAbstractClass but I can't find a solution to the following challanges:
How to "reserve" the ForeignKey fields in the abstract class (I know that I can't define them because there is no table in the database). I thought if using the contenttype framework but it seems to be inadequate because
it's a reference from one AbstractClass (Master) on one hand to another AbstractClass (Detail) on the other hand
for the user foreign key I know exactly to what model I want to reference
EDIT 1: Just realized that the 3rd challange is no problem at all, the following works:
class ContentLifecycleDetailClassModel (models.Model):
author = models.ForeignKey('auth.User', null=True)
class Test(ContentLifecycleDetailClassModel):
pass
CLI
from polls.models import Test
from django.contrib.auth.models import User
test = Test()
user = User.objects.first()
test.author=user
test.save()
x = Test.objects.first()
print x.author.username # <-- Working
EDIT 2:
This issue was already discussed in 2009 but only a solution for one abstract class is used in one implementation which does not fit these needs: http://djangotricks.blogspot.co.at/2009/02/abstract-models-and-dynamicly-assigned.html
EDIT 3:
I ended up using the ContentType Framework to solve (1) and (2)
masterModel_type = models.ForeignKey(ContentType, blank=True, null=True)
masterModel_id = models.PositiveIntegerField(blank=True, null=True)
master_Model = GenericForeignKey('masterModel_type', 'masterModel_id')
Solved the challenges by steps mentioned in the three edits. It was not the clean solution I expected but it is working..

Unable to load abstract profile model?

Up until recently, a project I'm working used one mega UserProfile to handle all profile data for two different types of users. Naturally this was messy, and it was about time to refactor it.
In my attempt to refactor the model, I split the model into Requester and Funder and created an abstract UserProfile model which both subclass:
class UserProfile(models.Model):
class Meta:
abstract = True
user = models.OneToOneField(User)
def __unicode__(self):
return unicode(self.user)
class Requester(UserProfile):
def requested(self, event):
"""Check if a user requested an event."""
return self == event.requester
class Funder(UserProfile):
osa_email = models.EmailField(null=True) # The e-mail of the contact in OSA
mission_statement = models.TextField(max_length=256)
And in my settings.py file, I adjusted the AUTH_PROFILE_MODULE.
AUTH_PROFILE_MODULE = "app.UserProfile"
The problem is, when hitting a page that uses "User.get_profile()" it breaks, reporting:
Unable to load the profile model, check AUTH_PROFILE_MODULE in your project settings
I'm not quite sure what's going on here. According to the docs, everything looks right.
Can some explain why this fails? (There are a bunch of alternative solutions I've come across, but I'd much prefer to fix this if possible than adopt some hack.)
What you are trying to do it not possible. AUTH_PROFILE_MODULE is expecting a concrete model, not an abstract one. Concrete means it has a table and can create instances. An abstract model can only be subclassed.
A logic reason why this not possible it that django has no one of knowing which model instance to return for your user. A Requester? A Funder? Simply being an abstract reference gives django no hints. One approach might be to look into the contenttypes framework and maybe come up with a generic UserProfile model containing a reference to the proper sub-profile type. You could then remove the abstract=True from your UserProfile, and create a generic relation to the specific Profile model. AUTH_PROFILE_MODULE would then simply reference that single UserProfile, but its instances can then use the .content_object to get the specific subobject.
There are many ways I'm sure you could address this problem, but I am just commenting on the reason why this specific approach does not work.

Django: When extending User, better to use OneToOneField(User) or ForeignKey(User, unique=True)?

I'm finding conflicting information on whether to use OneToOneField(User) or ForeignKey(User, unique=True) when creating a UserProfile model by extending the Django User model.
Is it better to use this?:
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
or this?:
class UserProfile(models.Model):
user = models.OneToOneField(User)
The Django Doc specifies OneToOneField, while the Django Book example uses ForeignKey.
James Bennett also has two Blog posts that providing conflicting examples as well:
Extending the User Model
User Registration
In the former post, Bennett provides some reasons why he switched to using ForeignKey instead of OneToOneField, but I don't quite get it, especially when I see other posts that recommend the opposite.
I'm curious to know your preference and why. Or, does it even matter?
The only real reason given in the article is that it can be set up so that the admin page for User will show both the fields in User and UserProfile. This can be replicated with a OneToOneField with a little elbow grease, so unless you're addicted to showing it in the admin page with no work at the cost of a bit of clarity ("We can create multiple profiles per user?! Oh no, wait, it's set unique.") I'd use OneToOneField.
Besides the admin page inlines, other reason for the ForeignKey solution is that it allows you to use the correct, default DB manager when objects are accessed with a reverse relation. Consider example from this subclasses manager snippet. Let's say that the Post class definition from the example looks like this:
class Post(ParentModel):
title = models.CharField(max_length=50)
onetoone = models.ForeignKey(SomeModel, unique=True)
children = ChildManager()
objects = models.Manager()
By calling somemodel_instance.post_set.all()[0], you get the desired subclasses objects of the Post class as indicated by defining the first (default) manager as a ChildManager. On the other hand, with OneToOneField, by calling somemodel_instance.post you get the Post class instance. You can always call somemodel_instance.post.subclass_object and get the same result, but the default manager could do any other sort of tricks and the FK solutions hides them nicely.
If you own and can modify the custom manager code you can use the use_for_related_fields attribute instead of using FK in place of legitimate 1to1 field, but even that can fail because of some not-known to me nuisances of the automatic managers. As far as I remember it will fail in the above example.
Other reason to generally not use the OneToOneField related to reverse relations: when you use reverse relations defined via OneToOneField you get an model instance, contrary to Manager for ForeignKey reverse relation and as a consequence there's always a DB hit. This is costly if you do some generic stuff on reverse relations (via _meta.get_all_related_objects()) and do not know and care if you will use them all or not.