Skip to content

Instantly share code, notes, and snippets.

@duahimanshu100
Last active September 16, 2019 17:29
Show Gist options
  • Save duahimanshu100/0bc9e08d2db4370fe492772ad33edecb to your computer and use it in GitHub Desktop.
Save duahimanshu100/0bc9e08d2db4370fe492772ad33edecb to your computer and use it in GitHub Desktop.
from datetime import datetime
from django.contrib.auth.models import User
def update_user_first_name(user_id, new_first_name):
return User.objects.filter(id=user_id).update(first_name=new_first_name)
def get_user_groups(user_id):
try:
user = User.objects.prefetch_related('groups').get(id=user_id)
except User.DoesNotExist:
return
return list(user.groups.values_list('name', flat=True))
def parse_str_date_to_date_obj(str_date, format='%Y-%m-%d'):
try:
# we can use utc timezone or the timezone from the settings
return datetime.strptime(str_date, format).replace(tzinfo=timezone.utc)
except ValueError:
raise ValueError('Provided Date format is not correct')
def count_user_joined_btw(date1, date2):
date1 = parse_str_date_to_date_obj(date1)
date2 = parse_str_date_to_date_obj(date2)
if date1 > date2:
smaller_date = date2
greater_date = date1
else:
smaller_date = date1
greater_date = date2
return User.objects.filter(date_joined__lte=greater_date).filter(date_joined__gte=smaller_date).count()
@regiohelden-dev
Copy link

update_user_first_name:
Fine. One query, pretty efficient. However the solution has some drastic differences in behavior from the usual get/save. Can you name them?

get_user_groups:
This would issue two queries. One to get the user and one to read their groups. Can you make it in one query?

count_user_joined_btw:
date_joined is a DateTimeField. Can you explain what happens when you compare a date (as returned by parse_str_date_to_date_obj) with a datetime? What would the condition of your query look like in SQL? How can you mitigate that behavior?
Also do you think it's a good idea to be able to pass in start and end date in an arbitrary order?

@duahimanshu100
Copy link
Author

Fine. One query, pretty efficient. However the solution has some drastic differences in behavior from the usual get/save. Can you name them?

update is used over queryset,so multiple objects can be updated in a single query whereas save is used over single object. if save is overridden, in case of update overridden save method is not called. Also one more thing, if I use get and the user with that id does not exist, the code will raise User.DoesNotExist .

@duahimanshu100
Copy link
Author

duahimanshu100 commented Sep 16, 2019

get_user_groups:
This would issue two queries. One to get the user and one to read their groups. Can you make it in one query?

We can use user = User.objects.prefetch_related('groups').get(id=user_id), then djano queryset will fetch groups with user in a single query

@duahimanshu100
Copy link
Author

duahimanshu100 commented Sep 16, 2019

count_user_joined_btw:
date_joined is a DateTimeField. Can you explain what happens when you compare a date (as returned by parse_str_date_to_date_obj) with a datetime? What would the condition of your query look like in SQL? How can you mitigate that behavior?

It will raise a warning something like date_joined received a naive datetime while time zone support is active. It will convert the date as per the TIME_ZONE in the settings to datetime object.
The SQL will be like where users.date_joined >= 2019-01-01 05:30:00 as per the timezone in the settings.

We can change this sql like this users.date_joined >= 2019-01-01 00:00:00 by converting the date to datetime timezone aware field by following code datetime.strptime(str_date, format).replace(tzinfo=timezone.utc) . Code has been updated

@duahimanshu100
Copy link
Author

Also do you think it's a good idea to be able to pass in start and end date in an arbitrary order?

Personally, I donot like them to be arbitrary, it increases probability of error. But in some cases, it helps.

@regiohelden-dev
Copy link

update_user_first_name:
Right. There's one more thing not executed on filter/update. It's quite similar to the overridden save method and might contain important business logic.

get_user_groups:
Are you sure? prefetch_related retrieves all related objects of a certain kind in one query. So all groups for all matches users. But still one query for the user and one query for the groups. Maybe try from the other side.

count_user_joined_btw:
If you don't like it, why did you made the parameters arbitrary?

So our SQL would now be date_joined >= '2016-01-01 00:00:00' AND date_joined <= '2016-04-01 00:00:00'.
This would exclude all users joined on the 1st of April except for the very first second of the day. There is an easy ORM tool that will simplify your query a lot.

@duahimanshu100
Copy link
Author

Right. There's one more thing not executed on filter/update. It's quite similar to the overridden save method and might contain important business logic.

Signals are also not called when you use update

@duahimanshu100
Copy link
Author

duahimanshu100 commented Sep 16, 2019

get_user_groups:
Are you sure? prefetch_related retrieves all related objects of a certain kind in one query. So all groups for all matches users. But still one query for the user and one query for the groups. Maybe try from the other side.

oops my bad, it was pretty simple
Group.objects.filter(user=user_id).values_list('name',flat=True)

@duahimanshu100
Copy link
Author

So our SQL would now be date_joined >= '2016-01-01 00:00:00' AND date_joined <= '2016-04-01 00:00:00'.
This would exclude all users joined on the 1st of April except for the very first second of the day. There is an easy ORM tool that will simplify your query a lot.

We can use __date
User.objects.filter(date_joined__date__lte=obj_datetime1.date()).filter(date_joined__date__gte=obj_datetime2.date()).

Query will be like django_datetime_cast_date("users_user"."date_joined", 'UTC') >= 2019-01-01 and django_datetime_cast_date("users_user"."date_joined", 'UTC') <= 2019-04-01

@duahimanshu100
Copy link
Author

count_user_joined_btw:
If you don't like it, why did you made the parameters arbitrary?

Because I don't know where this function is called from the application.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment