How to access user details in all pages in django? - python-3.x

After login I want to access User details when ever I want (Particularly in navigation bar).
I did not use user model provided in django. I created my own model like this for authentication.
My database is stored in mysql on phpmyadmin(Xampp).
AdminUser Model
class adminUser(models.Model):
username=models.CharField(max_length=50)
firstname=models.CharField(max_length=50)
department=models.CharField(max_length=50)
name=models.CharField(max_length=50)
mail=models.CharField(max_length=50)
id=models.IntegerField(primary_key=True)
password=models.CharField(max_length=200)
class Meta:
db_table="admin_users"
admin_users.py
def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username is not None and password is not None:
user=adminUser.objects.get(username=username)
hashed_password = user.password
is_check = bcrypt.checkpw(password.encode('utf8'),hashed_password.encode('utf8'))
print(is_check)
if is_check==True:
return redirect(reverse('faq_index'))
else:
return render(request,'AdminUsers/login.html')
return render(request,'AdminUsers/login.html')
During the login User Details can be only access by login function but I want to access user details in all pages for navigation bar and also want to find out whether user is authenticated or not. As I am not using User Model defined in django so I can not use user.is_authenticated(). So How do I do this?

First, inherit your user model from AbstractBaseUser. It gives you some features like is_authenticated, set_password and etc
Then define your custom user model as AUTH_USER_MODEL in your settings.py
After these, django treats to your custom user as its default user
models.py
from django.contrib.auth.base_user import AbstractBaseUser
class CustomUser(AbstractBaseUser):
...
settings.py
AUTH_USER_MODEL = 'myapp.MyUser'
Docs in https://docs.djangoproject.com/en/3.2/topics/auth/customizing/

I'm gonna recommend you take a step back, and extend the built-in User model instead of creating your own.
As mentioned by #Amin, you can extend the AbstractBaseUser class to create a custom User model, and override the AUTH_USER_MODEL in settings.py.
However, you can easily add a Profile model that has a one-to-one relationship with the built-in User model that does not require any overrides (which method is better escapes me at the moment, but I use the following method without issue):
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import models
UserModel = get_user_model()
class Profile(models.Model):
user = models.OneToOneField(UserModel, on_delete=models.CASCADE)
# add your desired fields here
department=models.CharField(max_length=50)
Then, in the same folder as contains your models.py, create a file called signals.py, and add the following code:
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Profile
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_user_settings(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
This will trigger the creation of a new Profile object whenever a User object is created.
Then, add the following to the same folder's apps.py (Assuming the app's name is users... make it match the name of the app that contains your Profile model):
from django.apps import AppConfig
class UsersConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'users'
def ready(self):
import users.signals
Now, after you run your migrations, you will be able to use the built-in User model, and add additional fields to it as desired in the Profile model.
The only downside is that it will require a little extra work if you want to modify the User model and the Profile model on the same page (ie. you won't just be able to specify the model name in an UpdateView).
I don't remember where I found this method. I believe it was adapted from this page on simpleisbetterthancomplex.com, but I'm not 100% sure.
At any rate, Django automatically creates a primary key id for you, so you probably don't need to manually define that in your model.

Related

Django get models by model name

In Django I know that there is one and only one model with name foo, but I don't know the app that this model is registered with! How can I get the model with only model_name?
As the answers to Django: Get model from string? show, you need either the app_name string or an AppConfig object to get a model from model_name.
However, you can use apps.get_app_configs() to iterate over AppConfig instances, so something like this is possible:
from django.apps import apps
for app_conf in apps.get_app_configs():
try:
foo_model = app_conf.get_model('foo')
break # stop as soon as it is found
except LookupError:
# no such model in this application
pass
Note that this iterable will include base django apps that are in INSTALLED_APPS like admin, auth, contenttypes, sessions, messages and staticfiles.

Add unique users to ProjectMember model in Django [duplicate]

This question already has answers here:
Django - create a unique database constraint for 2 or more fields together
(4 answers)
Closed 1 year ago.
I'm new to Django. Can anyone out there for the rescue.
In my current proect, I've couple of models inside a Django app: Project, ProjectMember. Here's their model and admin structure.
models.py
class Project(models.Model):
name = models.CharField(max_length=100)
start_date = models.DateField()
end_date = models.DateField()
description = models.TextField(max_length=1000)
class ProjectMember(models.Model):
project = models.ForeignKey(Project, on_delete=models.SET_NULL, null=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
admin.py
from .models import Project, ProjectMember
class ProjectMemberInline(admin.TabularInline):
model = ProjectMember
extra = 0
class ProjectAdmin(admin.ModelAdmin):
list_display = ('name', 'start_date', 'end_date')
inlines = [ProjectMemberInline]
admin.site.register(Project, ProjectAdmin)
What I want to achieve is, every Project(i.e. model) should have unique users(ProjectMember) and every user can be associated with multiple projects. But with the current structure, system allows duplicate ProjectMember insertion in the admin. Please see the screenshot, I've add here 2 same users.
Any help is appreciated.
What you have effectively done is define the "through" table for your own M2M relationship here. You have 2 options:
Redefine your tables to use an out of the box M2M relationship - https://docs.djangoproject.com/en/3.2/topics/db/examples/many_to_many/
Add a unique_together constraint - https://docs.djangoproject.com/en/3.2/ref/models/options/#unique-together
There's nothing wrong with what you've done, it's just a question of whether you want to use what Django gives you, or roll your own, so the solution of choice is up to you.
Edit: As the other answer points out, uniqie_together is deprecated infavour of UniqueConstraint, but the concept is the same, and which one you use may depend on the version of Django you're on. It sounds like this is a new project, so probably a recent version, in which case UniqueConstraint...
We can work with Django's constraint framework such that we can add a UniqueConstraint [Django-doc] over the two ForeignKeys:
class ProjectMember(models.Model):
project = models.ForeignKey(Project, on_delete=models.SET_NULL, null=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
class Meta:
constraints = [
models.UniqueConstraint(
fields=['project', 'user'],
name='onceperproject'
)
]
Most modern databases will enforce this, and thus prevent that you can add the same user twice per project, or the same project twice to a user.

How to add custom field to auth_permission model in django?

I am working with auth_permission model and to add extra fields in this model.
class Permission(models.Model):
name = models.CharField(_('name'), max_length=255)
content_type = models.ForeignKey(
ContentType,
models.CASCADE,
verbose_name=_('content type'),
)
codename = models.CharField(_('codename'), max_length=100)
Extra field I wanted to add is
application_id = models.ForeignKey(ApplicationTbl, db_column='application_id', on_delete=models.CASCADE, blank=True, null=False)
Can someone tell the work around to add it to auth_permission model.
To your models.py put this:
from django.contrib.auth.models import Permission
# inject application_id atribute to the Django Permission model
if not hasattr(Permission, 'application_id'):
application_id = models.ForeignKey(ApplicationTbl, db_column='application_id', on_delete=models.CASCADE, blank=True, null=False)
application_id.contribute_to_class(Permission, 'application_id')
After that, run migration and you should be ready to go.
The official standard way in Django docs to do this is to define it in class Meta inside your model; which if it's your user model, you could also consider customizing it by using PermissionsMixin;
which I believe is also applicable in your case if you put everything in its right place.
You have not provided much detail about your system and what you're trying to do but I guess you're trying to figure out in which application context the user is doing his activity. In that case and if your system is of a reasonably big scale, you could also try considering user authentication backend as a separate app folder in your Django project. hope I could help.

What will happen if I migrate django models directly from the mysql database

Earlier i had this as my model class.
class Certificate(models.Model):
User = models.AutoField(primary_key=True)
but after few months i added "data field" into my model class directly from the sql and without using migrations.
Now i am just wondering if i change the code in my models and again make migrations, will it work?
class Certificate(models.Model):
User = models.AutoField(primary_key=True)
data = models.TextField(null=True, blank=True)

Django Python 3.x - OneToOneField override delete() with ContentTypes

Scenario: there is different service types, i.e. clothes_washing, room_cleaning and room_maintenance.
Each one of these services have common fields like service_date for example.
Upon scenario I have model for each service and a Service model with common fields. the relation between Service model and each service model is OneToOneField.
I've tried to override the delete() function in Service model following this answer and It works for me but for only one service (like wheel in self.wheel.delete()). but if I want to delete upon the service type? how to achieve that approach?
my models.py:
class ClothesWashing(models.Model):
# special fields for clothes_washing
service = models.OneToOneField(Service, on_delete=models.DO_NOTHING, null=True)
class RoomCleaning(models.Model):
# special fields for room_cleaning
service = models.OneToOneField(Service, on_delete=models.DO_NOTHING, null=True)
class Service(models.Model):
# common fields for all services
def delete(self, *args, **kwargs):
#here I wanna "roomcleaning" attribute to be dynamic upon content type
self.roomcleaning.delete()
return super(self.__class__, self).delete(*args, **kwargs)
You can set the on_delete parameters to CASCADE:
class ClothesWashing(models.Model):
# special fields for clothes_washing
service = models.OneToOneField(Service, on_delete=models.CASCADE, null=True)
class RoomCleaning(models.Model):
# special fields for room_cleaning
service = models.OneToOneField(Service, on_delete=models.CASCADE, null=True)
The on_delete=… parameter [Django-doc] specifies what should happen when the item to which it refers is removed. So if Service is removed, and there is a ClothesWashing model that refers to it, then you can specify what to do.
By using CASCADE [Django-doc], you will remove the related ClothesWashing object as well, or as specified in the documentation:
Cascade deletes. Django emulates the behavior of the SQL constraint ON DELETE CASCADE and also deletes the object containing the ForeignKey.
It is better to implement it with this triggers, since methods like .delete() are not always called by the ORM when deleting in bulk. So Service.objects.all().delete() will delete all services, but will never call the .delete() method of your Service. By defining the triggers, you specify to Django what should happen with items that relate to it.
In this specific case, you perhaps might want to work with model inheritance [Django-doc]. Django can implement some boilerplate logic itself (like OneToOneFields to the parent model, etc.).
EDIT: If you want to delete the service if the given ClothesWashing, RoomCleaning, etc. are removed, you can override the .delete() method to delete that one too, you can for example make an abstract base class with:
class ServiceBase(models.Model):
# special fields for clothes_washing
service = models.OneToOneField(Service, on_delete=models.CASCADE, null=True)
def delete(self, *args, **kwargs):
service = self.service
super().delete(*args, **kwargs)
self.service.delete()
class Meta:
abstract = True
class ClothesWashing(ServiceBase):
# …
pass
class RoomCleaning(ServiceBase):
# …
pass
But likely if you use the ORM eventually some objects will not be removed, because of said ways to circumvent this.

Resources