Can i update in many to many field django - python-2.7

I want to update manytomany field, the others answers say than first remove and later add, but I need to know if is possible only update, may model:
class CtlgTypeExercise(models.Model):
type_exercise = models.CharField(max_length=50)
class CtlgExercise(models.Model):
exercise = models.CharField(max_length=250)
time = models.IntegerField()
ctlg_type_exercise = models.ForeignKey('CtlgTypeExercise')
class UsrExercisePlan(models.Model):
user = models.ManyToManyField(User)
date = models.DateField()
ctlg_exercise = models.ManyToManyField('CtlgExercise')
In CtlgTypeExercise has: Run, Swim, Bicycle .....
In CtlgExercise has: "Run 20 minutes in the street" and type is "Run", or "Run 10 minutes in the mountain", type "Run", or "Swim 30 minutes" type "Swim".....
In UsrExercisePlan save the user and exercise, for example user:"Peter", date:"Today", ctlg_exercise:"Run 20 minutes in the street" well the id. Now I want to replace or update ctlg_exercise per "Swim 30 minutes".
What is the best way to update, I dont want to remove and add, Thanks.

Ok, if you write:
usrExe = UsrExercisePlan.objects.get(user__id=idUser)
usrExe.ctlg_exercise = [newExercise]
automatically remove and add new Object, but I don't know with the id field, because it is increasing. Somebody know what happen with Django and longs id fields autoincrement, maybe django reset id field?

Related

Django Query - Get list that isnt in FK of another model

I am working on a django web app that manages payroll based on reports completed, and then payroll generated. 3 models as follows. (ive tried to limit to data needed for question).
class PayRecord(models.Model):
rate = models.FloatField()
user = models.ForeignKey(User)
class Payroll(models.Model):
company = models.ForeignKey(Company)
name = models.CharField()
class PayrollItem(models.Model):
payroll = models.ForeignKey(Payroll)
record = models.OneToOneField(PayRecord, unique=True)
What is the most efficient way to get all the PayRecords that aren't also in PayrollItem. So i can select them to create a payroll item.
There are 100k records, and my initial attempt takes minutes. Attempt tried below (this is far from feasible).
records_completed_in_payrolls = [
p.report.id for p in PayrollItem.objects.select_related(
'record',
'payroll'
)
]
Because you have the related field record in PayrollItem you can reach into that model while you filter PayRecord. Using the __isnull should give you what you want.
PayRecord.objects.filter(payrollitem__isnull=True)
Translates to a sql statement like:
SELECT payroll_payrecord.id,
payroll_payrecord.rate,
payroll_payrecord.user_id
FROM payroll_payrecord
LEFT OUTER JOIN payroll_payrollitem
ON payroll_payrecord.id = payroll_payrollitem.record_id
WHERE payroll_payrollitem.id IS NULL
Depending on your intentions, you may want to chain on a .select_related (https://docs.djangoproject.com/en/3.1/ref/models/querysets/#select-related)
PayRecord.objects.filter(payrollitem__isnull=True).select_related('user')
which translates to something like:
SELECT payroll_payrecord.id,
payroll_payrecord.rate,
payroll_payrecord.user_id,
payroll_user.id,
payroll_user.name
FROM payroll_payrecord
LEFT OUTER JOIN payroll_payrollitem
ON (payroll_payrecord.id = payroll_payrollitem.record_id)
INNER JOIN payroll_user
ON (payroll_payrecord.user_id = payroll_user.id)
WHERE payroll_payrollitem.id IS NULL

How to get Cartesian product of two tables in Django Queryset?

Is there a way to do the equivalent of a full outer join in Django (I think I've read that full outer joins are not supported).
My scenario is that I have three tables:
Staff / WeekList / WeeksCompleted
The relevant fields I'm trying to work with are:
Staff table - Staff Number.
WeekList table - Week Start date.
WeeksCompleted table - Week Start date and Staff Number.
Basically, everyone should have an entry in the WeeksCompleted table (if they're still active, that is, but that's not pertinent for this question). The queryset I would like to produce is a list of Staff who have missing weeks in the WeeksCompleted table.
I can get the result I want using SQL queries but it involves a full outer join on the Staff and WeekList tables. I was wondering if anyone knows of a way to do this using the queryset functions?
The only other way I can think to do the equivalent of the full join is to create a list using a nested loop of Staff Numbers against each week, which might have a sizeable processing overhead?
EDIT: if it helps, here are the three simplified models.
models.py
class Staff(models.Model):
staff_number = models.CharField(max_length=9, null=True)
class WeekList(models.Model):
week_start = models.DateField(null=True)
class WeeksCompleted(models.Model):
staff = models.ForeignKey(to='weekscompleted.Staff', null=True, on_delete=models.PROTECT)
week_list = models.ForeignKey(to='weekscompleted.WeekList', null=True, on_delete=models.PROTECT)
EDIT 2: The join I think I need is:
SELECT staff_number, week_start
FROM Staff, Contractor
GROUP BY staff_number, week_start
This will give a list of the expected weeks completed for staff:
week_start staff_number
17/10/2020 12345
17/10/2020 54321
I can then compare this to the WeeksCompleted table:
week_start staff_number
17/10/2020 12345
to find which staff are missing for a week using this query (keep in mind that this is a query I produced in a database):
SELECT qryShouldBeCompleted.week_start, qryShouldBeCompleted.staff_number
FROM qryShouldBeCompleted
LEFT JOIN WeeksCompleted ON qryShouldBeCompleted.staff_number =
WeeksCompleted.staff_number
AND qryShouldBeCompleted.week_start = WeeksCompleted.week_start
WHERE WeeksCompleted.staff_number Is Null
This would then produce the result I need:
week_start staff_number
17/10/2020 54321
Edit 3:
I just found an article on FilteredRelation that gets me partway there:
Staff.objects.annotate(missing=FilteredRelation('weekscompleted', condition=Q(weekscompleted__week_start='some date'))).values('staff_number', 'missing__staff__staff_number', 'missing__week_start')
which gets me this:
{'staff_number': '54321', 'missing__staff__staff_number': None, 'missing__week_start': None}
The only thing with this is that it only appears to work for one week at a time - using __lte in the condition doesn't return any 'None' values so I'd have to loop through each week...

Django ForeignKey and associated values in initial model

First off, sorry for this somewhat crappy title: I wasn't able to find a short and explicaive title for this one .
I'm currently trying to create an app where there will be players and each will have an inventory.
The inventory consists of a ManyToManyField of Items.
class Item(models.Model):
name = models.CharField(_('Name'), max_length=67)
flavor_text = models.TextField(_('Flavor text'))
class Inventory(models.Model):
items = models.ManyToManyField(Item, verbose_name=_('Items'))
I'm trying to implement a system where a player can, for example, have 3 bananas, 1 screwdriver and 17 loafs in his inventory.
As I'm very lazy, I don't want to enter manually 3 "banana" items in my inventory.
However, putting a quantity field in the Item model would be counter-productive, as I would need to create an Item named "3 bananas", another "2 bananas", again and again.
I also tought of creating Alice's Bananas, Bob's Bananas, and change the quantity, but still, that's creating too much banana items : especially if I need to add more players.
So, here is my question: How can I associate a value (here, a quantity) to a ForeignKey, but inside the model declaring the ForeignKey?
Thanks for reading!

Django filter by number of ForeignKey and less than a month in DateField

I have a model like this:
class MovieHistory(models.Model):
watched_by = models.ForeignKey(User)
time = models.DateTimeField(auto_now_add=True)
movie = models.ForeignKey(Movie)
I want to get up to 15 movies that were watched the most in the last 30 days. So far I have this:
Movie.objects.filter(time__gte=datetime.now()-timedelta(days=30))
How do you filter again, and order them by movie count? I know that I can filter the first 15 results like this: [:15], but I don't know how to order by the amount of movies in that model, and only pick one of each (so I don't have repeated MovieHistories with the same movies on each one).
Thanks.
Annotation is likely the best approach:
from django.db.models import Count
most_watched = Movie.objects.all().annotate(num_watched = Count('watched_by')).order_by('-num_watched')[:15]
I haven't tested this, but I believe this is on the way to the answer. Please let me know if it works! You may need to replace count('watched_by') by Count('watched_by_id') or whatever the field name is in your database (check with ./manage.py sql your_appname).
Hope this helps!
For more on using these annotations: https://docs.djangoproject.com/en/dev/topics/db/aggregation/#cheat-sheet

Django complex query without using loop

I have two models such that
class Employer(models.Model):
name = models.CharField(max_length=1000,null=False,blank=False)
eminence = models.IntegerField(null=False,default=4)
class JobTitle(models.Model):
name = models.CharField(max_length=1000,null=False,blank=False)
employer= models.ForeignKey(JobTitle,unique=False,null=False)
class People(models.Model):
name = models.CharField(max_length=1000,null=False,blank=False)
jobtitle = models.ForeignKey(JobTitle,unique=False,null=False)
I would like to list random 5 employers and one job title for each employer. However, job title should be picked up from first 10 jobtitles of the employer whose number of people is maximum.
One approach could be
employers = Employer.objects.filter(isActive=True).filter(eminence__lt=4 ).order_by('?')[:5]
for emp in employers:
jobtitle = JobTitle.objects.filter(employer=emp)... and so on.
However, loop through selected employers may be ineffiecent. Is there any way to do it in one query ?
Thanks
There is! Check out: https://docs.djangoproject.com/en/dev/ref/models/querysets/#select-related
select_related() tells Django to follow all the foreign key relationships using JOINs. This will result in one large query as opposed to many small queries, which in most cases is what you want. The QuerySet you get will be pre-populated and Django won't have to lazy-load anything from the database.
I've used select_related() in the past to solve almost this exact problem.
I have written such code block and it works. Although I loop over employers because I have used select_related('jobtitle'), I consider it doesn't hit database and works faster.
employers = random.sample(Employer.objects.select_related('jobtitle').filter(eminence__lt=4,status=EmployerStatus.ACTIVE).annotate(jtt_count=Count('jobtitle')).filter(jtt_count__gt=0),3)
jtList = []
for emp in employers:
jt = random.choice(emp.jobtitle_set.filter(isActive=True).annotate(people_count=Count('people')).filter(people_count__gt=0)[:10])
jtList.append(jt)