Creating a group hierarchy in Django - django

I can't seem to figure out how to best implement a user model following this schema in Django:
All are members, some are staff and must be part of a committee, where each committee has a leader
I'm creating a custom admin interface for my use. I've extended the AbstractBaseUser class to create a new class User that represents all members, but there are some fields that are not relevant to members.
class User(AbstractBaseUser):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
username = models.CharField(max_length=20, unique=True)
date_joined = models.DateTimeField(auto_now_add=True)
email = models.EmailField(max_length=100, default='')
phone_number = models.CharField(default='', blank=True)
balance = models.IntegerField(default=0)
minimum_balance = models.IntegerField(default=0)
status = models.CharField(choices=STATUS, default=STATUS_ACTIVE)
committee = models.CharField(choices=COMMITTEE, default=COMMITTEE_1)
objects = MyUserManager()
USERNAME_FIELD = 'username'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = ['first_name',
'last_name',
'email',]
def get_full_name(self):
return "{0} {1}".format(self.first_name,self.last_name)
def get_short_name(self):
return self.first_name
def __str__(self):
return self.username
These fields are not needed by a regular member:
status = models.CharField(choices=STATUS, default=STATUS_ACTIVE)
committee = models.CharField(choices=COMMITTEE, default=COMMITTEE_1)
Should these fields be included in the User class and be part of a Group, or should they be somewhere else? How can a leader of a committee be represented in this Django model? Also, is there a better way to implement a hierarchy of users like this in Django?

You can create a modal for committee like this.
Class Committee(models.Model):
name = models.CharField(max_length=255, unique=True)
members = models.ManyToManyField(User)
leader = models.ForeignKey(User)
Above models depict there will not be multiple committees with the same name, a committee will have some members and one leader.

Related

Django 4.0 - Trying to run a query based on result from another query

guys!
Here's what I'm trying to do:
I have a User model, a LinkedOrganization model, an Organization model that looks like this:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(db_index=True, unique=True)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
mobile = models.CharField(max_length=12)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_superuser = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name', 'mobile']
class Meta:
verbose_name = 'User'
verbose_name_plural = 'Users'
# Organization fields
class Organization(models.Model):
organization_name = models.CharField(max_length=50, unique=True)
def __str__(self):
return self.organization_name
class LinkedOrganization(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='linked_user')
organization = models.ForeignKey(Organization, on_delete=models.CASCADE, related_name='linked_organization')
is_manager = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
class Meta:
unique_together = (
("user", "organization")
)
In my API-view I'm trying to return all the users that are linked to an organization that the requester is a manager of.
Now, one requester can be a manager of 1 or more organizations, and I need to get all the users linked to those organizations. Meaning I need it to be sort of 'is member of organization A' OR 'is member of organization B', and iterate through all the organizations. Is it possible to do this with a Queryset in Django?
I realized when I wrote this that I could just get the organizations and use ReverseObjectManager to get the users of the organizations and iterate through those and make the dictionaries I need in Python, but if getting a query and use the result in a queryset is a function of Django that would be easier.
You may benefit from a slightly different database schema, not just for this query but in general. Consider the following:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(db_index=True, unique=True)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
mobile = models.CharField(max_length=12)
organization = models.ForeignKey('Organization', on_delete=models.CASCADE, related_name='user_organization')
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_superuser = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name', 'mobile']
class Meta:
verbose_name = 'User'
verbose_name_plural = 'Users'
# Organization fields
class Organization(models.Model):
organization_name = models.CharField(max_length=50, unique=True)
manager = models.ForeignKey(User, etc...)
def __str__(self):
return self.organization_name
In this scenario, every user has an organization, and every organization has a manager. Simple. Also note that I referenced the 'Organization' in the foreign key field as a string, since it is declared above where the Organization model is defined. With that, you can query like so:
managed_users = User.objects.filter(user_organization__manager=request.user)
A common practice is to create a profile model for the user, where additional fields like their organization, contact info, etc can be added. In that case you'd have something like this:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
organization = models.ForeignKey(Organization, etc...)

how to have database relationship with the active data. in django

i have some active and non active data's in EVENT models and active data has the VISITORS form to fill ..so far i have tried OnetoOne relationship but it didn't succeed ..i am getting both active and non active field in VISITORs model..thank you for your time.
here is models.py
class Event(models.Model):
event_id = models.AutoField
Event_Name = models.CharField(max_length=50)
description = RichTextField()
date_And_time = models.DateTimeField()
location=models.CharField(max_length=50)
slugs = models.SlugField(max_length= 200,default="")
picture = models.ImageField(upload_to='wildlife/picture', default="")
active = models.BooleanField(default=False)
class Meta:
ordering = ["date_And_time"]
def __str__(self):
return self.Event_Name
class Eventvisitor(models.Model):
event = models.OneToOneField(Event, on_delete=models.CASCADE, related_name="eventvistor",default="")
name = models.CharField(max_length=50)
email = models.CharField(max_length=70, default="")
phone = models.CharField(max_length=20,default="")
date = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-date']
def __str__(self):
return self.email
You can limit the choices with limit_choices_to=… [Django-doc]. But likely what you want is a ForeignKey, since otherwise, each Event can have at most one related EventVisitor (a OneToOneField is basically a ForeignKey with a unique=True constraint).
class Eventvisitor(models.Model):
event = models.ForeignKey(
Event,
limit_choices_to={'active': True},
on_delete=models.CASCADE,
related_name='eventvistors'
)
name = models.CharField(max_length=50)
email = models.CharField(max_length=70, default="")
phone = models.CharField(max_length=20,default="")
date = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-date']
def __str__(self):
return self.email
Note that while one can only select Events with active=True, if you later set the .active field to False, items will still link to it, since the relation is not enforced at database level.

Querying Parent from the foreign key django

I have the following models :
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=32)
last_name = models.CharField(max_length=32)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(default=timezone.now)
USERNAME_FIELD = 'email' # unique identifier, changed to email (default was username)
REQUIRED_FIELDS = ['first_name', 'last_name']
objects = CustomUserManager() # custom manager for interacting with database
def __str__(self):
return self.email
class Refer(models.Model) :
referred_by = models.ForeignKey(CustomUser, on_delete=models.CASCADE, default='admin', related_name='referred_by')
referrals = models.ManyToManyField(CustomUser, related_name='referrals', blank=True)
unique_ref_id = models.CharField(max_length=8, blank=True, default=generate())
def __str__(self) :
return f'Referred By: {self.referred_by}'
I want to implement referral system using this, I have unique_for_id field (example 'exbvagtl'), how can i create new referral under that user?
Something like : Refer.objects.create(referred_by= CustomUser.objects.get(Refer__unique_ref_id='exbvagtl'))
Better model designs, resources and improvements are heavily welcomed!
I think you are overcomplicating things. By making a Refer object that has a ForeignKey to CustomUser, this means that each CustomUser can have zero, one or more Refer objects. While that is not impossible to manage, it makes it harder, since now your views will need to make sure you are working with the correct Refer object, or have to "concatenate" these. Furthermore it means that a CustomUser can have multiple unique_ref_ids.
The question is why we need such object in the first place. You can simply construct a unique_ref_id on the CustomUser object, and add a ForeignKey to 'self' that specifies what the referring person was:
class CustomUser(PermissionsMixin, AbstractBaseUser):
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=32)
last_name = models.CharField(max_length=32)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(auto_now_add=True)
unique_ref_id = models.CharField(max_length=8, blank=True, default=generate)
referred_by = models.ForeignKey(
'self',
related_name='referrals',
null=True,
default=None,
on_delete=models.SET_NULL
)
USERNAME_FIELD = 'email' # unique identifier, changed to email (default was username)
REQUIRED_FIELDS = ['first_name', 'last_name']
objects = CustomUserManager() # custom manager for interacting with database
def __str__(self):
return self.email
Here we thus can add a user that was referred to by another user with:
CustomUser.objects.create(referred_by_id=id_of_referred_user)
If later the referred_by user is removed, then it will be set to NULL.
You can also query the users that have been referred by a user with:
myuser.referrals.all()
The problem is coming from your design of the referral system. Usually, in a referral system, one user has one referral_id. But your design suggests that one user can have multiple referral_ids.
I will suggest you move the unique_ref_id to the CustomUser model. That way you can do something like this:
Refer.objects.create(referred_by=CustomUser.objects.get(unique_ref_id="exbvagtl"))
2nd suggestion: put everything in one table
You will notice that the person referring another person is also a person, so it's basically a reference to self. In that regards you can have your CustomUser model look as follows:
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=32)
last_name = models.CharField(max_length=32)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(default=timezone.now)
referred_by = models.ForeignKey(self, on_delete=models.SET_NULL, blank=True, null=True)
unique_ref_id = models.CharField(max_length=8, blank=True, default=generate())
USERNAME_FIELD = 'email' # unique identifier, changed to email (default was username)
REQUIRED_FIELDS = ['first_name', 'last_name']
objects = CustomUserManager() # custom manager for interacting with database
def __str__(self):
return self.email
Base on that, you can do things like this:
Create a new user:
sponsor = CustomUser.objects.get(unique_ref_id="exbvagtl")
user = CustomUser(email=email, first_name=first_name, last_name=last_name,referred_by=sponsor)
user.save()
Get the list of a user's referrals:
sponsor = CustomUser.objects.get(unique_ref_id="exbvagtl")
referrals = CustomUser.objects.filter(referred_by=sponsor)
Hope this helps, if you have any doubts, don't hesitate to ask below.

How to save multiple object in a single POST request

I am new to DRF
I am saving a user details and his pets details .
Here is the model
class Users(models.Model):
id = models.AutoField(primary_key=True)
first_name = models.CharField(max_length=255, blank=True)
last_name = models.CharField(max_length=255, blank=True)
job = models.CharField(max_length=255, blank=True)
age = models.CharField(max_length=255, blank=True)
class PetDetails(models.Model):
user = models.ForeignKey(
Users, on_delete=models.CASCADE, blank=True, null=True)
pet_name = models.CharField(max_length=255, blank=True)
pet_color = models.CharField(max_length=255, blank=True)
pet_category = models.CharField(max_length=255, blank=True)
In this I need to save both user and his pets in a single Post request.
So I created a serializer like this
class UserCreateSerializer(ModelSerializer):
pet = PetDetailCreateSerializer(many=True)
class Meta:
model = Users
fields = ['first_name','last_name','job','age', 'pet']
def create(self, validated_data):
pets_data = validated_data.pop('pet')
user_obj = Users.objects.create(**validated_data)
for pet in pets_data:
PetDetails.objects.create(user=user_obj, **pet)
return user_obj
The issue I am facing is if a single person can have multiple pets.
For Example John is a user and he having two Pets.
So in this cases two users object will creating .How to resolve this
OR is there any other methods for handling this
My views is
class UserCreateView (CreateAPIView):
serializer_class = UserCreateSerializer
One way is to check if user already exists by using get_or_create:
user_obj = Users.objects.get_or_create(**audit_data)
But better way, I think, is to create user with multiple pets instead:
class UserCreateSerializer(ModelSerializer):
petdetails_set = PetDetailCreateSerializer(many=True)
class Meta:
model = Users
fields = ['first_name','last_name','job','age', 'petdetails_set']
def create(self, validated_data):
pets_data = validated_data.pop('pet')
user_obj = User.objects.create(**validated_data)
for pet in pets_data:
PetDetails.objects.create(user=user_obj, **pet)
return user_obj
Also, I advice to use separate endpoints/serializers for user creation.

Create relationship between two models using django class based views

I have two models Company and Campaign. I need to create a relationship between them. I think my models are fine.
companies/model.py
class Company(models.Model):
class Meta:
verbose_name_plural = "companies"
user = models.ForeignKey(settings.AUTH_USER_MODEL)
title = models.CharField(blank=False, max_length=128, default='')
slug = models.SlugField(blank=True, unique=True)
archived = models.BooleanField(default=False)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
campaigns/models.py
class Campaign(models.Model):
class Meta:
verbose_name_plural = "campaigns"
company = models.ForeignKey('companies.Company', on_delete=models.CASCADE,)
title = models.CharField(blank=False, max_length=128, default='')
slug = models.UUIDField(default=uuid.uuid4, blank=True, editable=False)
def __str__(self):
return str(self.title)
campaigns/forms.py
class CampaignForm(forms.ModelForm):
class Meta:
model = Campaign
fields = ['title','description','archived']
campaigns/views.py
class CampaignCreateView(SubmitBtnMixin, CreateView):
model = Campaign
company = None
form_class = CampaignForm
submit_btn = "Add Campaign"
template_name = "form.html"
campaigns/urls.py
url(r'^campaign/create/$', CampaignCreateView.as_view(), name='campaign-create'),
My question is, when creating a new campaign, where and how do I pick up the Company pk to populate the Campaign model? What is the most secure and best practice for doing this?
I found a solution but would like input on best practices still.
I added this to my CampaignCreateView
def form_valid(self, form):
company = get_object_or_404(Company, id=self.kwargs.get('pk'), user_id=self.request.user.id)
form.instance.company_id = company.id
return super(CampaignCreateView, self).form_valid(form)
and I changed my url to:
url(r'^campaign/(?P<pk>\d+)/create/$', CampaignCreateView.as_view()...
Not sure that I like the pk in the URL since it can be jacked. This is why I am filtering on the userid at the company model to make sure that the data is coming from the owner.
I thought of doing this by registering the company in the session id but I am not convinced that sessions do not present their own problems.