Issues with admin part - django

I am developing an app which extends the auth_user table, whenever any user register to the app, its one instance is created in the extended table,It works very well from simple url(manually), but when I login from admin side and add user it will also create the instance in extended table but does not show or take the user name, it just shows the blank row.
model.py code is as:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class Drinker(models.Model):
user =models.OneToOneField(User)
birthday =models.DateField()
name =models.CharField(max_length=100)
def __unicode__(self):
return self.name
#create our user object to attach to our drinker object
def create_drinker_user_callback(sender, instance, **kwargs):
drinker, new=Drinker.objects.get_or_create(user=instance)
post_save.connect(create_drinker_user_callback, User)
the last 3 lines three create the instance in extended table drinker but does not shows any value just take the user_id from auth_user table. it does not take name and birthday

You're not populating the Drinker.name field.
The row is blank because the unicode() method is returning nothing.
One way to solve this would be to set it explicitly in the signal handler:
def create_drinker_user_callback(sender, instance, **kwargs):
drinker, new=Drinker.objects.get_or_create(user=instance, name=instance.get_full_name())
post_save.connect(create_drinker_user_callback, User)
or ditch the (possibly redundant) name field altogether and normalise it a bit:
class Drinker(models.Model):
user = models.OneToOneField(User)
birthday = models.DateField()
def __unicode__(self):
return self.user.get_full_name()

Related

Multiple Tables for one Django Model

How can I dynamically create multiple tables for one specific model?
I have a model (e.g. "Data") and want each user to get a separate database table for this model.
There will be between 30 and 40 users, each with about 20 million entries in "Data". So that the database table does not get 800 million rows, I would like to split it.
Of course, it is also possible that there will be more in the future
Is this still possible with Django ORM?
I'm on PostgreSQL
Thanks :)
For a strict requirement, I had to go through this. I will just leave my solution here for future reference.
After User creation, let's say we want to create a "user_data_100" table with model "UserData" with pgsql database.
Note: here "100" is the "User" model(table entries) primary key value.
in app/model.py create the user model.
from django.db import models
from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver
from django.db import connection
from django.contrib.auth.models import User
class UserData(models.Model):
class Meta:
pass
dt_column_one = models.CharField(max_length=10,)
dt_column_two = models.CharField(max_length=5,)
def create_model_by_prototype(model_name, schema_name, tbl_name, prototype):
import copy
class Meta:
pass
setattr(Meta, "db_table", schema_name + "\".\"" + table_name)
fields = {}
for field in prototype._meta.fields:
fields[field.name] = copy.deepcopy(field)
attrs = {'__module__': "app.models.models", 'Meta': Meta}
attrs.update(fields)
model = type(model_name, (models.Model,), attrs)
return model
Note: function "create_model_by_prototype" will generate a model with given table name.
Now let's use the function. I assume the user will get created from the admin site. Let's create a post_save function to create UserData table.
#receiver(post_save, sender=User)
def create_user_data_table(sender, instance, *args, **kwargs):
print('User post save')
if kwargs['created']:
user_id = instance.id
dy_table_name = "user_data_" + str(user_id)
model= create_model_by_prototype("MyUserData", "public", dy_table_name, UserData)
with connection.schema_editor() as schema_editor: schema_editor.create_model(model)
else:
print('updated')
hope this works for you. Thanks.

Using signals for two models both inheriting from User in Django

Suppose we have two models that have signal to the User model:
from django.db import models
from django.contrib.auth.models import User
from django.db.models import signals
class Company(User):
name = models.CharField(null=True, blank=True, max_length=30)
if created:
Company.objects.create(
user_ptr_id=instance.id,
username=instance.username,
password=instance.password,
email=instance.email,
first_name=instance.first_name,
last_name=instance.last_name,
is_active=instance.is_active,
is_superuser=instance.is_superuser,
is_staff=instance.is_staff,
date_joined=instance.date_joined,
)
signals.post_save.connect(
create_company, sender=User, weak=False, dispatch_uid="create_companies"
)
class Individual(User):
name = models.CharField(null=True, blank=True, max_length=30)
def create_job_seeker(sender, instance, created, **kwargs):
"""
:param instance: Current context User instance
:param created: Boolean value for User creation
:param kwargs: Any
:return: New Seeker instance
"""
if created:
'''
we should add a condition on whether the Company uses the same username
if true, then, we must not create a JobSeeker and we would disable the account using
Firebase Admin
'''
JobSeeker.objects.create(
user_ptr_id=instance.id,
username=instance.username,
password=instance.password,
email=instance.email,
first_name=instance.first_name,
last_name=instance.last_name,
is_active=instance.is_active,
is_superuser=instance.is_superuser,
is_staff=instance.is_staff,
date_joined=instance.date_joined,
)
signals.post_save.connect(
create_job_seeker, sender=User, weak=False, dispatch_uid="create_job_seekers"
)
Now, each time a User is created we should be allowed to extend it through both Individual and Company models. But, I want to prohibit the usage of both objects. User can either have a Company or an Individual object to be edited not both. Should I override the save method such as this:
def save(self, *args, **kwargs):
if not Company.objects.filter(username=self.username).exists():
super(Model, self).save(*args, **kwargs)
else:
raise 'Some error'
Or should I add a condition on the created method such as this:
...
if created and Company.objects.filter(username, self.username).exists() == False:
Company.objects.create(
...
Which approach is better? And is there another approach that you might suggest?
Signals, for most cases, I believe are the best way to handle sharing data between models assuming the CRUD for each related model isn't done together. So post_save, pre_save, post_delete, pre_delete and so are typically the best way to go about handling data that any given model instance relies on. This can be true about manipulating model data after a save. Signals were designed specifically for this reason. The other great thing about signals is you can connect them throughout your project and not necessarily just where the Model is defined. Just import the model and the signal you want to connect to it and bam!
How to use signals? follow django's documentation here. it's very simple
https://docs.djangoproject.com/en/4.0/topics/signals/

Querying Django model object

I can query the user's profile using the user_id in the UserProfile model below. But I would like to use the username in the django.contrib.auth.models.User.username in the urls instead.
The urls.py currently looks like:
path('profile/<int:user_id>', login_required(views.UserProfileDetail), name='userprofile'),
So as against the above urls, I would like to use something like:
path('profile/<str:username>', login_required(views.UserProfileDetail), name='userprofile'),
Any idea how to write the query correctly? All attempts so far returns an error
views.py:
def UserProfileDetail(request, user_id, *args, **kwargs):
profile = UserProfile.objects.get(user_id = user_id)
profile_display = {'profiledetail': profile }
return render(request, 'main/userprofile.html', context=profile_display)
this is what the model looks like:
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='userprofile')
userPhone = models.IntegerField(blank=True, null=True)
location = models.CharField(max_length=100, blank=True, null=True)
You can accept both a user_id and a username with a default value, and then query with Q objects to look for a UserProfile where at least one of the two matches. The paths guarantee that one is passed:
from django.contrib.auth.decorators import login_required
from django.db.models import Q
#login_required
def UserProfileDetail(request, user_id=None, username=None):
profile = UserProfile.objects.get(
Q(user_id=user_id) | Q(user__username=username)
)
profile_display = {'profiledetail': profile }
return render(request, 'main/userprofile.html', context=profile_display)
You might want to use login_required as a decorator instead. This avoids wrapping the view each time in a login_required.
Note: Functions are normally written in snake_case, not PerlCase, therefore it is
advisable to rename your function to user_profile_detail, not UserProfileDetail.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

Django Display User as user Full Name in Admin Field

I got stuck when trying to display User foreign as use full name
models.py
class Customer(models.Model):
name = models.charfield(max_length=100)
sales = models.foreignkey(User, on_delete=models.CASADE)
admin.py
#admin.register(Customer)
list_display = ('name', 'sales')
nah by default sales as user foreign display as username. But i want display it to first name or full name
Edited
You can add a method in the ModelAdmin, as mentioned by #dirkgroten in the comments below:
from django.contrib import admin
#admin.register(Customer)
class CustomerAdmin(admin.ModelAdmin)
list_display = ('name', 'sales_first_name')
def sales_first_name(self, obj):
return obj.sales.first_name + obj.sales_last_name
The upside is you can handle null values as you wish, if your foreign key is not required, e.g.
def sales_first_name(self, obj):
if obj.sales:
return obj.sales.first_name
else:
return None # or return 'nobody'
Old answer
(which, as mentioned in the comments, does not work (unless you use a plugin like django-related-admin))
Maybe use sales__first_name instead?
list_display = ('name', 'sales__first_name')
This way django will evaluate the first_name attribute of the user object instead of returning the default string representation (defined by the __str__ method in the user model, which returns just the username).

How To get just only the added this object when many to many table is updated

from django.contrib.auth.models import User
from django.db.models.signals import post_save, m2m_changed
from django.dispatch import receiver
# Create your models here.
class FollowersModel(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1, related_name='usr')
follow = models.ManyToManyField(User, related_name="whom")
my_followers = models.ManyToManyField(User, related_name='my_followers')
update = models.DateTimeField(auto_now=True)
timestamp = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return str(self.user)
#receiver(m2m_changed, sender=FollowersModel.follow.through)
def follow_by_follow_add_or_remove(sender, **kwargs):
if kwargs.get('action') == 'post_add':
print kwargs.get('instance').follow.all()
**solved part**
print list(kwargs.get('pk_set'))[0]
# then it returns the id of the added object :)))
In this example when I print kwargs.get('instance').follow.all() then I can get the whole lists of follow label but I just want to get only the added one, I mean I am searching kind of print kwargs.get('instance').follow.this(), just like jquery we do 'this.append()' only the added one
Okey Bros It is Solved
solved part is in the codes :))
From the documentation of m2m_changed signal:
pk_set
For the pre_add, post_add, pre_remove and post_remove actions, this is a set of primary key values that have been added to or removed from the relation
You can access pk_set from the kwargs to check which primary keys values have been added/removed.
You can use this pk_set to fetch the corresponding objects from database.