Django Python m2m_change does not work when removing the relation - python

I am writing a project related to courses. However, the save() does not work when I use Django admin to add CourseSession. But when I edit it and then save, it will work. Please help me. Followings are class CourseSession and Course. What I am doing here is to update instructors of each course automatically when related course session is added.(update: I have used the m2m_change function but it wont work when removing course session)
def course_session_instructor_changed(sender, instance, action, **kwargs):
superCourse = instance.course
superCourse.instructors.clear()
course_session_set = superCourse.course_session.all()
for each_course_session in course_session_set:
# add instructor
if action=="post_add":
instructors = each_course_session.instructors.all()
for instructor in instructors:
if not instructor in superCourse.instructors.all():
superCourse.instructors.add(instructor)
# remove instructor
elif action=="pre_remove" :
if not each_course_session == instance:
instructors = each_course_session.instructors.all()
for instructor in instructors:
if not instructor in superCourse.instructors.all():
superCourse.instructors.add(instructor)
superCourse.save()
m2m_changed.connect(course_session_instructor_changed, sender=CourseSession.instructors.through)
class CourseSession(models.Model):
course = models.ForeignKey('Course', related_name='course_session')
instructors = models.ManyToManyField(User, related_name = 'instructor_course_session')
enrollment = models.ManyToManyField(User, related_name = 'course_enrollment')
start = models.DateField()
# Weeks of duration
duration = models.IntegerField()
# capacity of the session
max_cap = models.IntegerField()
questionSet = models.ManyToManyField(QuestionSet, blank=True, null=True, related_name='session_questionSet')
class Meta:
verbose_name = _('Session')
verbose_name_plural = _('Sessions')
get_latest_by = "start"
def __unicode__(self):
return unicode(self.instructors.all())+unicode(self.course)+unicode(self.start)
def is_started(self):
return date.today()> self.start
def is_expired(self):
length = timedelta(days = self.duration*7)
return self.start+length< date.today()
def get_enrollment(self):
return self.enrollment.count()
**class Course(models.Model):
name = models.CharField(_('Course Name'),max_length=256)
# Simple Introduction
brief_intro = models.CharField(_('Brief Intro'),max_length=1024)
intro = models.TextField()
learning_obj = models.TextField()
creator = models.ForeignKey(User, related_name = 'course_creator')
created = models.DateTimeField(auto_now_add=True)
cover = models.ImageField(upload_to = 'course/covers/')
institute = models.ForeignKey('Institute', related_name='institute_courses')
workload = models.IntegerField()
assignments = models.IntegerField()
exams = models.IntegerField()
knowledge_tree_root = models.ForeignKey(Topic, related_name='knowledge_tree_root')
instructors = models.ManyToManyField(User, related_name='courses', null=True, blank=True)
#tree_root = models.ForeignKey('Topic')
class Meta:
verbose_name = _('Course')
verbose_name_plural = _('Courses')
def __unicode__(self):
return self.name

It won't work the first time, as many-to-many fields can't be set until the instance has been saved once (since they're saved in a separate table, and have to have an ID to link to). So the admin doesn't set the values until after the save.
Rather than override save, you probably want to use the m2m_changed signal.

Related

Interactive drop down list django with option to add new entry

I am relatively new to django. I have made a simple app which enable a user to record details regarding a run (Activity). The user enters data such as: distance, time, route name, date ran..etc using a django ModelForm.
I have a ModelForm which enables the logged on user to add an Activity to the model database.
How do I get the form to add a drop down list of all the routes which are already in the model database AND add a new one if the Person is entering data about a route they haven't ran before.?
class Person(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
fName = models.CharField(max_length =100)
lName = models.CharField(max_length =100)
weight = models.FloatField()
sex = models.CharField(max_length=10)
def __str__(self):
return self.lName
class Activity(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
length = models.FloatField()
runTime = models.DurationField()
route = models.CharField(max_length=100)
ave = models.DurationField()
dateRun = models.DateField()
marathon = models.DurationField()
def __str__(self):
return self.route ```
I would add model Route in models.py:
class Person(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
fName = models.CharField(max_length=100)
lName = models.CharField(max_length=100)
weight = models.FloatField()
sex = models.CharField(max_length=10)
def __str__(self):
return self.lName
class Route(model.model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
length = models.FloatField()
route = models.CharField(max_length=100)
class Activity(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
runTime = models.DurationField()
route = models.ForeignKey(Route, on_delete=models.CASCADE)
ave = models.DurationField()
dateRun = models.DateField()
marathon = models.DurationField()
def __str__(self):
return self.route
When person wants to add activity he will have all routes that he enters before. That solution require additional job in modelform, and need of another form to add new route. I think the ActivityForm should look like:
class ActivityForm(forms.ModelForm):
class Meta:
model = Activity
fields = '__all__'
widgets = {'person': forms.HiddenInput}
and init it in views.py:
form = ActivityForm(initial={'person': request.user.id})
form.field['route'].queryset = Route.objects.filter(person=request.user)

Django-filter how to show only some objects on dropdown?

My site simply works like this: every Manager can have some SubManagers, those SubManagers can have some Agents (so the Agents are indirectly related to the Manager, see models.py to understand better the relations between them). I want to show in the Manager's profile page (see views.py) all the MembershipCard created by his/her related Agents. I'm trying to implement a filter to search, for example, cards created by a specific Agent, i'm able to do this but i would like to show in the dropdown only the Agents related to the Manager, the dropdown list now shows all Agents in the database
models.py
class StandardProfile(models.Model):
name = models.CharField(max_length=200, null=True)
surname = models.CharField(max_length=200, null=True)
phone_number = models.CharField(max_length=200, null=True)
class Meta:
abstract = True
class Manager(StandardProfile):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
class SubManager(StandardProfile):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
manager = models.ForeignKey(Capo, null=True, on_delete = models.SET_NULL)
class Agent(StandardProfile):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
manager = models.ForeignKey(Manager, null=True, on_delete = models.SET_NULL)
subManager = models.ForeignKey(SubManager, null=True, blank=True, on_delete = models.SET_NULL)
class MembershipCard(models.Model):
agent = models.ForeignKey(Agent, null=True,blank=True, on_delete = models.SET_NULL)
client = models.ForeignKey(Client, null=True,blank=True, on_delete = models.SET_NULL)
creation_date = models.DateTimeField(auto_now_add=True, null=True)
activation_date = models.DateTimeField(null=True,blank=True)
expiration_date = models.DateTimeField(null=True,blank=True)
views.py
#login_required(login_url='login')
def profilePage(request, pk): #www.mysite.com/profilePage/<pk>
user = User.objects.get(id=pk) #getting the user from <pk>
cards = MembershipCard.objects.filter(agent__manager=user.manager)
myFilter = MembershipCardFilter(request.GET,queryset=cards,user=user)
cards = myFilter.qs
#page_obj is used for Pagination, and contains the cards, i removed this part of code for better readability, can add it if needed
context = {'page_obj': page_obj,"user": user,"myFilter":myFilter}
return render(request, 'polls/profilePage.html',context)
filters.py
class MembershipCardFilter(django_filters.FilterSet):
class Meta:
model = MembershipCard
fields = ['agent','agent__subManager']
exclude = ['creation_date']
By reading answers to similar questions i think i have to modify the __init__ method in the CardFilter class, i've tried to adapt some answers to my case but it didn't work for some reasons . Any anser/comment is appreciated!
PS: I don't know if the title is clear, feel free to suggest a better one
You can try feeding the agent dropdown during init like (not tested!):
class MembershipCardFilter(django_filters.FilterSet):
agent= django_filters.ModelChoiceFilter(
queryset=Agent.objects.none(),
)
class Meta:
model = MembershipCard
fields = ['agent','agent__subManager']
exclude = ['creation_date']
def __init__(self, *args, **kwargs):
user = kwargs.get("user")
agents = Agent.objects.filter(manager__user=user)
super().__init__(*args, **kwargs)
self.filters["agent"].queryset = agents

How to create a function that counts total string objects in my ArrayField column for my model?

Trying to create a column in my model called, stock_count, that finds the sum of the total string objects in my ArrayField(), aka stock_list. Here is my function.
def total_stocks_calc(self):
self.stock_count = Bucket.objects.aggregate(Sum('stock_list', distinct=True))
self.save()
However it doesn't seem to be doing anything, no calculating, leaving the field blank in my model, admin page, and DRF interface...
EDIT: updated post with new implementation.
Here is my model.
class Bucket(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='buckets')
users = models.ManyToManyField(settings.AUTH_USER_MODEL)
category = models.CharField(max_length=30, choices=category_options)
name = models.CharField(max_length=35)
created = models.DateTimeField(default=timezone.now)
slug = models.SlugField(unique=True, blank=True)
stock_count = models.IntegerField(blank=True, null=True)
stock_list = ArrayField(models.CharField(max_length=6,null=True),size=30,null=True)
about = models.CharField(max_length=75)
objects = models.Manager()
bucketobjects = BucketObjects()
class Meta:
ordering = ('-created',)
def total_stocks_calc(self):
self.stock_count = Bucket.objects.annotate(stock_count=F('stock_list__len'))
self.save()
def __unicode__(self):
return self.stock_list
Would like to know the proper way to count total items in ArrayField(), thank you in advance.
The ArrayField provides the len lookup field, through that you can get the count
like
from django.db.models import F
Bucket.objects.annotate(stock_count=F('stock_list__len'))

(Django) How to assign unique values to each user for something linked by ForeignKey?

My group project for school has us building a school management system. I have the following models:
Student:
class Student(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
User.is_student = True
enrolled_courses = models.ManyToManyField(Session, blank=True)
def __str__(self):
return f'{self.user.last_name}, {self.user.first_name}'
Session:
class Session(models.Model):
course = models.ForeignKey(Course, on_delete=models.CASCADE)
course_date_start = models.DateTimeField()
course_date_end = models.DateTimeField()
def session_id(self):
new_session_date = self.course_date_start.strftime('%Y')
return f'{new_session_date}{self.course.number}{self.pk}'
def __str__(self):
return f'{self.course.number} - {self.course.title} - {self.session_id()}'
Assignment:
class Assignment(models.Model):
session_link = models.ForeignKey(Session, on_delete=models.CASCADE)
created_date = models.DateTimeField(default=timezone.now)
due_date = models.DateField()
title = models.CharField(max_length=50)
total_points = models.IntegerField()
points_earned = models.IntegerField(blank=True, null=True)
objective = models.TextField()
def __str__(self):
return self.title
The problem right now is if I save one value of points_earned to a user, it saves that value to all, since they're linked by the FK.
What's the best way to handle it so each Student can have their own score for each assignment?
If you want each Student to have their own score for each assignment,then a solution would be to have a table to keep track of the score with those two models as foreign keys.
Use https://docs.djangoproject.com/en/3.1/topics/db/models/#intermediary-manytomany
class StudentAssignment(models.Model):
student = models.ForeignKey(Student, on_delete=models.CASCADE)
assignment = models.ForeignKey(Assignment, on_delete=models.CASCADE)
points_earned = models.IntegerField(blank=True, null=True)

'Registered_Courses' object has no attribute 'course_set' Django

I am really stuck on this error and it does not make sense why it does not follow the relationship backward on Registered_Courses on the foreign key for Courses when i use course_set
views.py
def registered_coursesView(request, username):
'''Page to display the registered courses of a user.'''
registeredCourses = Registered_Courses.objects.get(owner = request.user)
courseInfo = registeredCourses.course_set.all()
context = {'registeredCourses': registeredCourses, 'courseInfo':courseInfo}
return render(request, 'safetyCourseApp/registered_courses.html', context)
models.py
class Course(models.Model):
'''Offered Course information.'''
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
name = models.CharField(max_length=200, primary_key=True)
description = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
start_date = models.DateField()
end_date = models.DateField()
price = models.DecimalField(max_digits=10, decimal_places=2)
capacity = models.IntegerField()
registered_ppl = models.IntegerField()
def __str__(self):
"""Return a string representation of the model."""
return self.name
class Registered_Courses(models.Model):
"""Something specific learned about a Course."""
registered_course = models.ForeignKey(Course, on_delete=models.CASCADE, null=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
def __str__(self):
"""Return a string representation of the model."""
return f'{self.owner}'
Please let me know what you guys think. I cannot think of a reason why this is not working. Thanks!
As you have specified in your models, each Registered_Courses will have a FK to Course, So each Course can have multiple Registered_Courses.
But you are trying to get multiple Course objects from a single Registered_Courses
The backward relationship is something like:
>>> course = Course.objects.first()
>>> course.registered_courses_set

Categories

Resources