Add db_index to Django User model? - django

I have a custom user model that takes in the following fields:
class MyUser(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=60, unique=True)
email = models.EmailField(max_length=120, unique=True)
full_name = models.CharField(max_length=50, blank=True)
is_active = models.BooleanField(default=True)
I know username and email automatically adds a db_index because it is set to unique=True.
Does it make sense to add db_index=True for my full_name and is_active fields?
I have a search feature that checks if the regex matches the full_name. In my model managers, I also filter against is_active=True.
I would like to optimize querys, but don't want to add tables to my database if it isn't necessary.
Thank you!

Yes,just add db_index=True into your field like this
full_name = models.CharField(max_length=50, blank=True,db_index=True)
After you migrate it, your database will create a index for this filed.
Refer this: https://docs.djangoproject.com/en/1.9/ref/models/fields/#db-index
Field.db_index
If True, a database index will be created for this field.

Whether the index is used for full_name if you search using a regex is doubtable. You want to make sure of that by looking at the output of the query planner (e.g. postgres: EXPLAIN ANALYZE select * from myuser where user=...;).
If it is not used (for example because the regex lower cases everything) then you have to create a different index. For example on the lower cased value or even a GIN index (for partial matches).
Django Admin search query not hitting Postgres index
http://blog.it-agenten.com/2015/04/tuning-django-orm-text-queries/

Related

How to change a field type in a Django model with existing data?

I have the following existing twitter field on the extended UserProfile model and I'd like to change the field type from a URLField to a CharField with max_length of 20. When I try to migrate the change, I get the error django.db.utils.DataError: value too long for type character varying(20). I do not care about existing data in that field and prefer they go blank if there is existing data when migrating. How can I change the field type and clear existing data?
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.SET_NULL, null=True)
# old field
twitter = models.URLField(verbose_name="Twitter", blank=True)
# new field
# twitter = models.CharField(
# max_length=20, verbose_name="Twitter Username", null=True, blank=True
# )
Approach 1st
In order to change to type and maximum length of a field which should know the longest url length in your db.
If you are using sqlite3 database then run the following query in your db_shell or sqlite3 shell or whatever you have.
SELECT MAX(LENGTH(column_name)) FROM table_name;
and then set the max_length of the new field to that value.
Approach 2nd
python manage.py shell
and then
# Import your model
profiles = UserProfile.objects.all()
max_length = 0
for userprofile in userprofiles:
if len(userprofile.twitter) > max_length:
max_length = len(userprofile.twitter)
print(max_length)
and then set your max_length to the value printed from the above code.
This error means that there are values on that column, that are longer than 20 characters.
You need to track and change them. This will change them into an empty string, so existing data will be lost for good.
UPDATE <table_name>
SET twitter = ""
WHERE LENGTH(twitter) > 20;
Then you can run you migrations.

Django Saving ForeignKey as Character instead of Integer Field

I have two models.One of the models has a pk of unique identifying strings. Sometimes it would be something like 'TTL123' and sometimes '000010'. For some reason when I created the foreignkey fields is using integers and the item '000010' shows up as 10. I can't save 'TTL123'. Why has django created the table as integer instead of character field? How do I change it? I've been looking at documentation and can't find answer.
class Item(models.Model):
item_id = models.CharField(max_length=255, primary_key=True)
# ... other fields...
class ItemRestriction(models.Model):
item = models.ForeignKey(Item, related_name='item', on_delete=models.PROTECT, blank=True, null=True)
How can I make the ForeignKey use a character field instead of integer? Now when I try to access item__item_id I get nothing, because it's 000010 in Item table and 10 on ItemRestriction. I don't understand why it's doing this.
You can use the to_field.
class ItemRestriction(models.Model):
item = models.ForeignKey(Item, related_name='item', to_field='item_id', on_delete=models.PROTECT, blank=True, null=True)
see: https://docs.djangoproject.com/en/3.0/ref/models/fields/#django.db.models.ForeignKey.to_field
If possible, you might want to consider using an AutoField or some sort of field that handles unique=True and auto increments on creation.

How to filter the results of a query passing a string as a field

I need to append the mail_d_list only if the flag is set in the model.
I am calling this function with the airport_code already coming in from the user. Now I would like to add the user to the email list if they have the flag selected for the user or not.
Each user in the model has six boolean flags, one flag for each report possible. The text for the flag is in the middle.
I have tried .get() and .filter()
Models.py
class Aerodrome(models.Model):
''' Aerodrome model for storing three-letter airport codes iata,
airport description and the database partition informtion. '''
iata = models.CharField(max_length=3, primary_key=True)
customer = models.CharField(max_length=5)
name = models.CharField(max_length=100)
partition_code = models.CharField(max_length=6)
class DistributionList(models.Model):
''' List of all email addresses that are to receive
automated emails from the system '''
email = models.CharField(max_length=40, primary_key=True)
name = models.CharField(max_length=40)
receive_emails = models.BooleanField()
receives_report = models.ManyToManyField(Aerodrome)
script.py
for user in DistributionList.objects.filter(receives_report__iata=airport_code):
mail_d_list.append(user.email)
This is definitely the wrong approach.
You already have the aerodromes defined in a separate model. You should define a ManyToManyField as a relationship between them, rather than dynamically defining fields on your DistributionList model. Then your script can filter by that relationship.

Python/Django recursion issue with saved querysets

I have user profiles that are each assigned a manager. I thought using recursion would be a good way to query every employee at every level under a particular manager. The goal is, if the CEO were to sign in, he should be able to query everyone at the company - but If I sign on I can only see people in my immediate team and the people below them, etc. until you get to the low level employees.
However when I run the following:
def team_training_list(request):
# pulls all training documents from training document model
user = request.user
manager_direct_team = Profile.objects.filter(manager=user)
query = Profile.objects.filter(first_name='fake')
trickle_team = manager_loop(manager_direct_team, query)
# manager_trickle_team = manager_direct_team | trickle_team
print(trickle_team)
def manager_loop(list, query):
for member in list:
user_instance = User.objects.get(username=member)
has_team = Profile.objects.filter(manager=user_instance)
if has_team:
query = query | has_team
manager_loop(has_team, query)
else:
continue
return query
It only returns the last query that was run instead of the compiled queryset that I am trying to grow. I've tried placing 'return' before 'manager_loop(has_team, query) in order save the values but it also kills the loop at the first non-manager employee instead of continuing to the next employee.
I'm new to django so if there is an better way than recursion to pull the information that I need, I'd appreciate suggestions on that too.
EDIT:
As requested, here is the profile model.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=30, blank=False)
last_name = models.CharField(max_length=30, blank=False)
email = models.EmailField( blank=True, help_text='Optional',)
receive_email_notifications = models.BooleanField(default=False)
mobile_number = models.CharField(
max_length=15,
blank=True,
help_text='Optional'
)
carrier_options = (
(None, ''),
('#txt.att.net', 'AT&T'),
('#messaging.sprintpcs.com', 'Sprint'),
('#tmomail.net', 'T-Mobile'),
('#vtext.com', 'Verizon'),
)
mobile_carrier = models.CharField(max_length=25, choices=carrier_options, blank=True,
help_text='Optional')
receive_sms_notifications = models.BooleanField(default=False)
job_title = models.ForeignKey(JobTitle, unique=False, null=True)
manager = models.ForeignKey(User, unique=False, blank=True, related_name='+', null=True)
Ok, so it's a hierarchical model.
The problem with your current approach is this line:
query = query | has_team
This reassigns the local name query to a new queryset, but does not reassign the name in the caller. (Well, that's what I think it's trying to do - I am a little rusty but I don't think you can just | together querysets like that.) You'd also need something like:
query = manager_loop(has_team, query)
to propagate the changes via the returned object.
That said, while Django doesn't have built-in support for recursive queries, there are some third party packages that do. Old answers eg (Django self-recursive foreignkey filter query for all childs and Creating efficient database queries for hierarchical models (django)) recommend django-mptt. Your tag mentions postgres, so this post might be relevant:
https://two-wrongs.com/fast-sql-for-inheritance-in-a-django-hierarchy
If you don't use a third-party approach, it should be possible to clean up the evolution of the queryset - cast it to a set and use update or something, since you're accumulating profiles. But the key error is not using the returned modified object.

Relationships with Django and rest framework add replace fields

My user object with rest framework has an avatar_id and a cover_id. But Instead of displaying that to the API, I want it to be the actual avatar URL and cover URL already.
My User model:
avatar_id = models.IntegerField()
cover_id = models.IntegerField()
My UserAvatar model:
id = models.IntegerField(primary_key=True)
user_id = models.IntegerField()
file_id = models.IntegerField()
My Files model:
id = models.IntegerField(primary_key=True)
filename = models.CharField(max_length=255)
Same concept with UserCover.
How do I remove the avatar_id from the results of /users/ and add a avatar field with the actual avatar filename?
I'm not sure I understand your question correctly, but here what I think the problems are. Reading your question, I assumed that you are a beginner, so I answered as such. Sorry if it's not the case.
You don't need to add the id fields, it's done automatically by Django because all tables need a primary key. You define a PK only when you need to name it something else than 'id'.
You should really read the Django tutorial which explains how to define models. User.cover_id and UserAvatar.file_id should be defined as ForeignKey. If you don't know what a foreign key is, then stop playing with Django and read a database tutorial before.
There's already a model and a set of classes to manage your users in Django. You should use them. For example, a "user profile" is the right way to extend the user model.
If you force the users to choose one avatar in a set of predefined avatars, then what you want to do is ok. If the users can choose any avatar (upload), then you should use OneToOneField or put it directly in the user model (or profile).
I don't know what is a UserCover, but here's what your models could look like:
from django.contrib.auth.models import User
class UserProfile(models.Model):
# Link to Django normal User (name, email, pass)
user = models.ForeignKey(User, unique=True)
# Any information that a user needs, like cover, wathever that is, age, sexe, etc.
avatar = models.CharField(max_length=255)
Or like this if a will be reused often :
class Avatar(models.Model):
# name = ...
# description = ...
path = models.CharField(max_length=255)
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
avatar = models.ForeignKey(Avatar, unique=True)
# other data