I've been trying to learn django for the past couple of days, and I've made a basic bug tracking software as of now (using the admin part of django)
Ive created a table called bugs:
from django.db import models
# Create your models here.
class Bugs(models.Model):
STATUS_CHOICE = (
('Work In Progress', 'Work In Progress'),
('Incomplete', 'Incomplete'),
('Completed', 'Completed')
)
Project_Name = models.CharField(max_length=100)
Basic_Description = models.CharField(max_length=100)
Detailed_Description = models.TextField(default='The Description, here.')
Status = models.CharField(max_length=18, choices=STATUS_CHOICE)
Assigned_to = models.CharField(max_length=100)
Reported_by = models.CharField(max_length=50, blank=True, null=True)
Reporters_Mail_ID = models.CharField(max_length=50, blank=True, null=True)
Reported_Date = models.DateTimeField(null=True, blank=True)
Created = models.DateTimeField(auto_now_add=True, null=True, blank=True)
Updated = models.DateTimeField(auto_now=True, null=True, blank=True)
Deadline_Date = models.DateTimeField(null=True, blank=True)
image = models.FileField(null=True, blank=True)
def __str__(self):
return self.Project_Name + ' [' + self.Status + '] ' + self.Basic_Description + ' [' + self.Assigned_to + ']'
class Meta:
verbose_name_plural = "Bugs"
How do I make it so that when a project name is selected, the Assigned_to is also automatically selected?
And how do I make sure that once it is assigned to the person/ Status is edited/ or any sort of edit is made by the SuperUser, a mail is sent to the person it is assigned to, and when an edit is made by the Person it is assigned to, the super user gets a mail saying an edit has been made?
Not that important but, I was also wondering if there is any way to make sure that when the Super user Assigns some work to one of the admins, Only the admin specified can view the Issue??
UPDATE:
Let me try to be more descriptive this time..
So i have 2 types of users:
1st is the Super User who decides what bugs should be placed into the list and also manages the other users as well.
2nd is the other normal users, selected by the superuser (Users can only edit the posts added by the superuser)
The database has 12 columns out of which Project_Name and Assigned_To are 2.
My question is,
How do I make it such that when the superuser enters the Project_Name, the Assigned_To Column is already filled?
take this as an example
Project_Name: Project1 ---> Assigned_To: Person1
Project_Name: Project2 ---> Assigned_To: Person2
Project_Name: Project3 ---> Assigned_To: Person3
Project_Name: Project4 ---> Assigned_To: Person4
Now if the Super User Decides that the bug is in Project 1, and Project 1 is handled by Person 1, How do i make the program such that If Project 1 is chosen, Person 1 will automatically be selected in the Assigned_To Column?
And Once the SuperUser Selects the SAVE option(taking the same case as above). How do I make the program send a mail to Person1 when ever SuperUser Updates the database? And how do I make the program send a mail to the SuperUser when the Person1 Makes changes to the Database?
Well I'm not sure what your questions really are, but as I understood, you first need to add another field for user as following :
from django.contrib.auth import get_user_model
user = models.ForeignKey(get_user_model(), blank=True, null=True)
regarding sending email when an object of the model is saved or updated, you should override the save() method of the model and put the code you want to execute.
You can use django signals,
When the SuperUser saves or edits. You can write your code to achieve whatever you want.
from django.db.models.signals import post_save
def send_email(sender, instance, created, **kwargs):
# your code goes here...
post_save.connect(send_email, sender=Bugs)
ref: https://docs.djangoproject.com/en/2.0/ref/signals/#post-save
Ok, so first off, it's common practice to use all lower case for model fields. Python has a style guide called PEP8 which documents all the best practices for writing python. So take a look here to get started; https://www.python.org/dev/peps/pep-0008/#descriptive-naming-styles
Now if your assigned_to model should reference a user for the model to relate to you should indeed do as Milad said & use a ForeignKey to the User model because there are many benefits to using relationships where it makes sense for your data.
If you want the assigned_to to be automatically filled based on the project_name then you'd have to use javascript. You'd probably send an AJAX request on selection of the project to go to the backend with the project value. Then send back the assignee for that project which would be used in the javascript to set the value of the assigned_to field.
As far as sending emails go when changes are made to your model, you should use signals in python. Signals get fired when changes are made to objects, for example;
from django.core.mail import send_mail
from django.db.models.signals import post_save
from django.dispatch import receiver
from bugs.models import Bugs
#receiver(post_save, sender=Bugs)
def bugs_post_save(sender, instance=None, created=None, update_fields=None, **kwargs):
if created or 'assigned_to' in update_fields:
# We have a new object or the assignee has changed
send_mail(
'Subject here',
'Here is the message.',
'from#example.com',
['to#example.com'],
fail_silently=False,
)
The docs on email sending are here
Your receiver functions like this can be included underneath your models or in a specific signals.py module which you'd import in the application config. That would like something like;
# bugs/apps.py
from django.apps import AppConfig
class BugsConfig(AppConfig):
name = 'bugs'
verbose_name = "Bugs"
def ready(self):
# importing signals
import signals
Related
I'm trying to build a simple ToDo app in django:
A 'box' contains multiple 'tasks', has an 'owner' (the person who created the box and has administrative privileges) and multiple 'users' (persons who can access the box and add/edit tasks).
I want to automatically add the owner to the users set. I've tried this multiple ways:
Overwriting the save():
class Box(models.Model):
owner = models.ForeignKey(User, related_name='boxes_owned', on_delete=models.CASCADE, null=True, blank=False)
users = models.ManyToManyField(User, related_name='boxes_assigned')
title = models.CharField(max_length=50)
description = models.TextField(null=True, blank=True)
def save(self):
super().save()
self.users.add(self.owner)
This does not work and neither does working with a post-save signal.
#receiver(post_save, sender=Box)
def update_box(sender, instance, **kwargs):
instance.users.add(instance.owner)
The owner won't be added to the users set.
What am I doing wrong, how can I fix this?
Is this the right approach at all?
EDIT:
It seems like the owner is added correctly to users by the save function as well as by the update_box function. At least that is what the debugger tells me. However, when I try to edit the box afterwards the owner is not in the users array.
Are there any events after post_save that could mess with my relationships?
I am upgrading a large Django-based app from Django 1.7 app to Django 2.2 and am having a lot of trouble with a permissions-related mixin.
class PrincipalRoleRelation(models.Model):
"""A role given to a principal (user or group). If a content object is
given this is a local role, i.e. the principal has this role only for this
content object. Otherwise it is a global role, i.e. the principal has
this role generally.
user
A user instance. Either a user xor a group needs to be given.
group
A group instance. Either a user xor a group needs to be given.
role
The role which is given to the principal for content.
content
The content object which gets the local role (optional).
"""
:::
user = models.ForeignKey(User, verbose_name=_(u"User"), blank=True, null=True, on_delete=models.SET_NULL)
group = models.ForeignKey(Group, verbose_name=_(u"Group"), blank=True, null=True, on_delete=models.SET_NULL)
role = models.ForeignKey(Role, verbose_name=_(u"Role"), on_delete=models.CASCADE)
:::
However, this fails to load during app initialization because User, Group, and Role etc are also apps whose loading is in progress and "populate() is not re-entrant" (so Dango complains)
I tried to work round this by amending the above code to create a sort of "skeleton" class which does not attempt to reference any other apps, e.g. :
app_models_loaded = True
try:
from django.contrib.auth import get_user_model
User = get_user_model()
except:
app_models_loaded = False
if app_models_loaded:
from django.contrib.auth.models import Group
user = models.ForeignKey(User, verbose_name=_(u"User"), blank=True, null=True, on_delete=models.SET_NULL)
group = models.ForeignKey(Group, verbose_name=_(u"Group"), blank=True, null=True, on_delete=models.SET_NULL)
role = models.ForeignKey(Role, verbose_name=_(u"Role"), on_delete=models.CASCADE)
:::
Then in manage.py I would define the full mixin class, called say PrincipalRoleRelation2 and overwrite the skeleton class via the code :
from django.contrib import admin
from permissions.models import PrincipalRoleRelation
if admin.site.is_registered(PrincipalRoleRelation):
admin.site.unregister(PrincipalRoleRelation)
admin.site.register(PrincipalRoleRelation, PrincipalRoleRelation2)
However, although this almost seems to work, I am not seeing some of the PrincipalRoleRelation2 attributes, "role" for example, in what I hoped would be the re-mapped PrincipalRoleRelation class with all attributes present.
I feel I am digging myself into an ever deeper hole, and that the above approach is unsound and will never work properly. So any help would be very much appreciated!
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
edit: In response to schillingt's comment, the User class is defined as follows:
class User(AbstractBaseUser): # , PermissionsMixin):
""" Custom user model
Currently just used by the tests for django-permissions
All unique user fields required for a user
NB: Fields that are customizable across multiple identities will be part of a Profile object
"""
# Dont use PermissionsMixin since not using contrib.auth.models.Permissions
# and not using authentication backend perms ... so its only relevant for groups
# ... however it causes user.groups relations name clashes ..
# But we are using the groups part with django-permissions:
groups = models.ManyToManyField(Group, verbose_name=_('groups'),
blank=True, help_text=_('The groups this user belongs to. A user will '
'get all permissions granted to each of '
'his/her group.'),
related_name="user_set", related_query_name="user")
is_superuser = models.BooleanField(_('superuser status'), default=False,
help_text=_('Designates that this user has all permissions without '
'explicitly assigning them.'))
username = models.EmailField(_('Email (Username)'), max_length=255, unique=True)
# Make username an email and just dummy in email here so its clearer for user.email use cases
As a solution for circular refferrence, django has an ability to specify ForeignKey (or any other relational field) with string refferrence to related model, instead of importing the actual class.
user = models.ForeignKey('users.User', on_delete=models.CASCADE)
This is imo the recommended way to define related fields.
I am just learning Django so I thought of creating a project called job board to understand more in detail. I have drawn the following use case.
People can register as job seekers, build their profiles and look for
jobs matching their skillsets
Companies can register, post jobs.
Multiple representatives from a company should be able to register
and post jobs.
Independent Recruiter can create an account as well.
The company can contact to that independent recruiter.
How would be the model design for such a use case? I am confused with the multiple user types in Django. Some favors creating a user profile, while some favors using Groups.
For now, I could only do the following
class User(AbstractUser):
'''
Abstract user because django recommends to start with custom user
'''
username = None
email = models.EmailField(_("Email Address"), unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
def __str__(self):
return self.email
class Company(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
I could create a model for User and Company. But I have no idea on handling multiple user types like user can be either job seeker or recruiter. Also, multiple representatives from a company should be able to register and post jobs as well as there can be independent recruiter as well. How would you handle such a case if you have to? Can anyone help me in a step by step guide, please? This way it will clear my confusion and will help me in better design of tables in the future.
Update with example in a nutshell
class User(models.Model):
'''
User can be of any 3 types or can have multiple role as well
'''
is_job_seeker = models.BooleanField(default=False)
is_recruiter = models.BooleanField(default=False)
is_mentor = models.BooleanField(default=False)
class Company(models.Model):
user = models.ForeignKey(User) # only user with is_recruiter flag active can be
class JobSeeker(models.Model):
user = models.OneToOneField(User)
# job seeker profile related fields like experiences, skills, education, profile image etc
class Recruiter(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
company = models.ForeignKey(Company, null=True, blank=True)
# recruiter related profile
Your implementation is almost there. It doesn't look like you need a custom user model right now, so I would just use Django's default.
I would have something like:
from django.conf import settings
from django.db import models
class Company(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
# Other company-related fields
class JobSeeker(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
# Other jobseeker-related fields
class Recruiter(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
company = models.ForeignKey(Company, null=True, blank=True)
# Other recruiter-related fields
When you create any of the models above, you can assign them a user account; and for recruiter, you can assign the company they work for. For example, a company named stack_overflow can have its own company account with a username/password/etc. Then, recruiters who work for stack_overflow could also have their own accounts with their own username/password/etc. Running a command like stackoverflow.recruiter_set will give you all recruiters who work for stack_overflow.
Note that I do not reference User directly. Using the above approach makes your life easier if you decide to switch User models in the future.
I am assuming you don't want to create a User, then create a Company and link it to that user - you just want to do it in one go. That's a slightly different question and the solution will involve you creating a sign-up Form, or something of that sort, where you can add some logic about whether the user is a company, recruiter or jobseeker.
Regarding your other points, it looks like you're looking to set user permissions. Here are the docs for setting default permissions for your custom users, and here are the general docs for Django's built-in permissions system. For example, your Company and Recruiter model could return True for has_perm('your_app.add_job'), while your Jobseeker model returns False. I.e. Companies and Recruiters can create Jobs, but jobseekers cant.
Hope this helps!
I created a working "Log in with facebook" setup with django-allauth. However, I want those new user accounts that allauth creates to not be activated automatically.
Basically I want to activate all new accounts from the Django admin pages.
What would be a best practice solution to this?
Thanks
How do you know that an account is activated or not? You need some kind of field in your User model.
Let's say that your field is called is_activated:
class User(BaseUser):
is_activated = models.BooleanField(default=False)
and that's it. This field's default value will be False which means that a created user is not activated by default.
Then, you can add this field to the admin page and toggle it from there.
Hope it helps!
I'd suggest what follows because when I made cogofly I had this huge problem where some people (few but some of them do) click sometimes on "log in with google" and sometimes on "log in with facebook" and this is the same person! So be careful with that and this solution will help you to avoid such problem. The only thing to remember: there's only one primary key for all social networks: the email.
Like it's written in the documentation, I'd make a OneToOne link to the User model.
From this I would suggest this:
precise the date of the first login
precise the date of the last login
precise if the account has been validated
Like this:
from django.contrib.auth.models import User
class Person(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
is_activated = models.BooleanField(default=False)
And make a social login model where you precise how the user logged in:
class SocialLogin(BaseModel):
S_GOOGLE = 1
S_FACEBOOK = 2
TAB_S_LOGIN = {
S_GOOGLE: _("Google"),
S_FACEBOOK: _("Facebook"),
}
logged_in = models.IntegerField(
choices=[(a, b) for a, b in list(TAB_S_LOGIN.items())],
default=S_GOOGLE)
date_first_login = models.DateTimeField(auto_now_add=True,
verbose_name=_('First login'))
date_last_login = models.DateTimeField(auto_now=True,
verbose_name=_('Last login'))
person = models.OneToOneField(Person, on_delete=models.CASCADE)
def logged_in_desc(self):
return SocialLogin.TAB_S_LOGIN[self.logged_in]
def __str__(self):
return '{} / {}'.format(
self.logged_in_desc(),
description if self.description is not None else '?')
I have a Profile model for custom user data:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, null=False)
# more stuff...
I also have a Notifications app that allows models to send notifications and emails to users.
I want users to have the option to turn on or off different notifications, but I DON'T want to add a massive list of boolean fields to the Profile like this:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, null=False)
send_model1_notice1 = models.BooleanField()
send_model1_email1 = models.BooleanField()
send_model1_notice2 = models.BooleanField()
send_model1_email2 = models.BooleanField()
send_model2_notice1 = models.BooleanField()
send_model2_email1 = models.BooleanField()
send_model3_notice1 = models.BooleanField()
send_model3_email1 = models.BooleanField()
# etc...
where modelx is some model or some other app that is the origin of the notification, and noticex/emailx is some specific cause for notification.
I was thinking a more sustainable way of doing this would be to create a ProfileOptions model that external models could use to define their own notifications settings.
This way, when I add a new app/model, I could link its notification sources to the ProfileOptions model somehow and have these options to turn them on or off magically appear in the user's profile.
Does this make sense? If so, is it possible? If so is it a good idea? and if so, what structure should I use to connect the models, ProfileOptions, and the user's Profile?
Obviously I'm hoping for an answer to the last question, but I don't want to rule out the possibility that the answer to the others might be "no".
One approach would be to have a separate Notification model which links the two:
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class Notification(models.Model):
# Foreign key to user profile
user = models.ForeignKey(Profile)
# Generic foreign key to whatever model you want.
src_model_content_type = models.ForeignKey(ContentType)
src_model_object_id = models.PositiveIntegerField()
src_model = GenericForeignKey('src_model_content_type', 'src_model_object_id')
# Notification on or off. Alternatively, only store active notifications.
active = models.BooleanField()
This approach would let you handle an arbitrary number of source models for notifications (each with an arbitrary number of notifications), without trying to squeeze all the notification information into your user profile.