I am having a problem in editing a page in my code.
Basically I have a page where I have multiple select field where I can select the students. But I have a problem in understanding how to remove a specific student, if I need to edit this page. Let me be more clear with some code.
models.py
class TheorySyllabus(models.Model):
name = models.CharField(max_length=100, null=True, blank=True)
subject_duration = models.ManyToManyField(
SubjectDuration, related_name='theory_syllabus')
course_type = models.ForeignKey(
CourseType, on_delete=models.DO_NOTHING, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = 'Theory Syllabus'
class TheoryCourse(models.Model):
name = models.CharField(max_length=100)
student = models.ManyToManyField(Student, related_name='theory_courses')
theory_syllabus = models.ForeignKey(
TheorySyllabus, on_delete=models.DO_NOTHING, null=True, blank=True)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
views.py
def edit_theory_course(request, pk):
theory_course = TheoryCourse.objects.get(id=pk)
student_obj = theory_course.student.all()
theory_syllabus = TheorySyllabus.objects.all()
students = Student.objects.filter(is_active=True).order_by('last_name')
context = {
'theory_course': theory_course,
'theory_syllabus': theory_syllabus,
'students': students,
'student_obj': student_obj,
}
if request.method == 'POST':
course_name = request.POST.get('course_name')
student = request.POST.getlist('student')
syllabus = request.POST.get('syllabus')
try:
theory_course.name = course_name
theory_course.theory_syllabus_id = syllabus
theory_course.save()
for stud in student:
theory_course.student.add(stud)
theory_course.save()
messages.success(request, "Theoretical Course Edited")
return HttpResponseRedirect(reverse('theory:course_list'))
except:
messages.error(request, "Failed to Edit Theoretical Course")
return HttpResponseRedirect(reverse('theory:edit_theory_course', kwargs={'pk': pk}))
return render(request, 'theory/edit_theory_course.html', context)
I know that basically, what I need to do is in the view to place an if statement to compare the two lists and remove (if needed) the value that is not part of the entry list anymore. The problem is that I have no clue how to place this logic.
Any help is highly appreciated.
Thank you very much
I think you can replace the full list of students at once by doing theory_course.student.set([list_of_student_pks])
A very clean and easy way to accomplish this is to make a query for all of the current students (which you don't even need to do since it's already in theory_course), then just simply get the difference between the students in that query and the submitted students. This will yield you the students that were removed, which you can then iterate over and delete.
current_students = theory_course.students
selected_students = Student.objects.filter(pk__in=student)
removed_students = current_students.difference(selected_students)
for s in removed_students:
s.delete()
There is however an option to set all of the IDs at once using and Object's set() method, but having the list of removed users so that you can make necessary changes (such as sending emails) is much better from my experience.
Related
I've been looking for my problem in Django documentation and couldn't find solution.
My problem is that in Api Pannel I cannot insert more objects from "ActorsAndDirectors" class into "cast" Field in "Movie" class. I can only insert one. How to transfrom cast field so I could insert multiple objects from "ActorsAndDirectors" class into "Movie" class
this is the code
`
class ActorsAndDirectors(models.Model):
name = models.CharField(max_length=32, blank=False, null=False)
surname = models.CharField(max_length=32, blank=False, null=False)
role = models.CharField(max_length=11, blank=False, null=False)
def __str__(self):
return f"{self.name} {self.surname}"
class Movie(models.Model):
title = models.CharField(max_length=50, blank=False, null=False, unique=True)
description = models.TextField(max_length=400)
cast = models.ForeignKey(ActorsAndDirectors, on_delete=models.CASCADE)
premiere = models.DateField()
updated = models.DateTimeField()
slug = models.SlugField()
def number_of_ratings(self):
return Rating.objects.filter(movie=self).count()
def avg_rating(self):
score = 0
ratings = Rating.objects.filter(movie=self)
for rating in ratings:
score +=rating.stars
if len(ratings) > 0:
return score/len(ratings)
else:
return 0
def __str__(self):
return f"{self.title}, ({self.premiere})"
`
I looked through Django documentation for some kind of list Field but with no good results. Im looking for help how to transform the field or maybe some other explanation of my problem
What you are looking for is a Many to Many relation. Where many actors and directors can participate in many different movies.
I would like to complement that when querying the database its slower to look for strings. Maybe you should check this choices option for your ActorsAndDirectors role field.
This would help if you try to filter directors or actors later on. Another option would be a Table and a FK.
I'm creating a page that lets only admin add some assets. Each asset has a type. I have used a dropdown to select the asset_type. The selected value of asset_type gets passed into views.py but I can't get it written into the newly created asset object.
Here is my models.py
class assetType(models.Model):
title = models.CharField(max_length=150)
#property
def get_type(self):
return asset.objects.filter(asset_type=self.id)
def __str__(self):
return f"{self.title}"
class Meta:
verbose_name_plural = 'Asset Types'
class asset(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, null=False)
asset_type = models.ForeignKey('assetType', on_delete=models.CASCADE, null=True)
asset_name = models.CharField(max_length=30, null=True) #unique=True
location = models.CharField(max_length=30, null=True)
brand = models.CharField(max_length=30, null=True)
purchase_year = models.PositiveIntegerField(blank=True, null=True)
isActive = models.BooleanField(default=True, null=True)
currentOwner = models.ForeignKey(User, default='', null=True, on_delete=models.CASCADE)
Here is createAssetView from views.py
#user_passes_test(lambda u: u.is_superuser)
def createAssetView(request):
assetTypeList = assetType.objects.all() # use assetType.title
assettype = request.POST.get('asset-type')
assetname = request.POST.get('asset-name')
locationn = request.POST.get('location')
brandd = request.POST.get('brand')
purchaseyear = request.POST.get('purchase-year')
isActivve = request.POST.get('is-active','') == 'on'
cuser=request.user
context={
"cuser":request.user,
"asset_type_list":assetTypeList,
"asset_type":assettype,
"asset_name":assetname,
"location":locationn,
"brand":brandd,
"purchase_year":purchaseyear,
"isActive":isActivve,
'iterator':range(2014,2050)
}
if request.method == 'POST':
new_asset = asset()
new_asset.asset_type_title=request.POST.get('asset-type')
new_asset.asset_name=assetname
new_asset.location=locationn
new_asset.brand=brandd
new_asset.purchase_year=purchaseyear
new_asset.isActive=True if isActivve else False
new_asset.currentOwner=cuser
print(assettype) # PRINT assettype
new_asset.save()
return redirect('createAssets')
return render(request, 'assets/createAsset.html', context)
The PRINT assettype statement prints selected asset type from the form, so the value is getting passed to the view, how should I populate the table with it?
Any suggestions or help will be highly appreciated. Thanks!
new_asset.asset_type_title=request.POST.get('asset-type')
This is the line where your problem is. You can't assign a foreign object like that, it does not automatically search for a match in another model.
You need to either provide the object to be used as a foreign key, or the object's primary key.
So we find our database entry for the selected type:
target_type = assetType.objects.get(title=assettype)
# this assumes you use unique titles for each assetType, see below
and then provide the new asset either with the object itself:
new_asset.asset_type = target_type
or its primary key:
new_asset.asset_type_id = target_type.pk
I strongly reccomend spending more time with django's documentation to strengthen your understanding of how foreign object relation is implemented in django. At least review the tutorials that focus on that.
Also consider the following:
The title field of the assetType model is not limited to be unique. What happens if two assetTypes with the same title are created?
Repetition of request.POST.get('asset-type')
i have a model course and i also have a model review and rating that allows users rate a particular course. now i want to count all the ratings on each course when i filter. NOTE: in the courses detail view i figured out a way to count the ratings like this rating_count = CourseRating.objects.filter(course=course, rating__in=["3.0", "4.0", "5.0"]).count(). This only worked in the detail view because i get the course first using course = Course.objects.get(slug=course_slug).
Now i want to count the rating in course lists view, how can i do this while using filter?
this is how the detail view looks like
#login_required
def course_details(request, course_slug):
user = request.user
course = Course.objects.get(slug=course_slug)
reviews_count = CourseRating.objects.filter(active=True, course=course).count()
rating_count = CourseRating.objects.filter(course=course, rating__in=["3.0", "4.0", "5.0"]).count()
this is how the list view look like NOTE: this is where i want to count all the rating of each course
def index(request):
courses = Course.objects.filter(course_publish_status="published").order_by('?')
models.py
class Course(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
course_title = models.CharField(max_length=100, null=True, blank=True)
slug = models.SlugField(unique=True)
course_category = models.ForeignKey(Category, on_delete=models.DO_NOTHING, null=True, blank=True, related_name="courses")
course_publish_status = models.CharField(max_length=10000, choices=COURSE_PUBLISH_STATUS, default="in_review")
class CourseRating(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
course = models.ForeignKey(Course, on_delete=models.CASCADE, null=True, related_name="ratings")
rating = models.CharField(max_length=1000, choices=USER_COURSE_RATING)
review = models.TextField()
date = models.DateTimeField(auto_now_add=True)
active = models.BooleanField(default=False)
def __str__(self):
return f"{self.course.course_title} - {self.rating}"
class Meta:
verbose_name = "Course Reviews and Ratings"
What you're looking for is Aggregation the docs have some pretty good (and quick) examples of how to achieve it.
You're looking to use an .annotate with the Count object. Judging by your filtering for ratings, you'll also want to use the filter= parameter of the Count object.
I also highly suggest you look into how to properly use the Q() object.
With all that being said, an example to achieve what you're looking for:
courses = Course.objects.filter(
course_publish_status="published",
).annotate(
rating_count=Count(
'ratings',
filter=Q(ratings__rating__in=["3.0", "4.0", "5.0"])
)
).order_by("?")
Keep in mind that I considered you have a related_name="ratings" on your ForeignKey. For your next questions I strongly suggest sharing the models you're working with as well (or at least the relevant portions of them).
So below I have this code where the request has the current User object. A User can have "followers" which are represented by FollowUser and they can follow another user's goal, which is represented by FollowGoal. I want to return all the posts that the current user which is represented by request.user. I want to be able to get all posts where the creator is someone the current user is following or is a goal the current user is following. As you can see below I generate a list of UUID for both the followees of the current user (people they are following) as well as a list of UUID for the goals the current user is following. Then I do <attribute>__in, but for some reason it's returning an empty list. Even if I just filter by followees or follow goals. Not sure why it's returning an empty list. I generated fake data that represent both the cases of posts created by followees of the current user and goals the current user is following. The Post object should filter if the creator is by someone the current user is following OR the goal is a goal the currrent user is following.
view.py
def get_current_user_followee(request):
current_user = request.user
followee_list = list(FollowUser.objects.filter(follower=current_user).values_list('followee', flat=True))
followee_list.append(current_user.uuid)
return [str(followee) for followee in followee_list]
def get_current_user_join_goal_follow(request):
current_user = request.user
join_goal_list = list(FollowGoal.objects.filter(follower=current_user).values_list('uuid', flat=True))
return [str(join_goal_follow) for join_goal_follow in join_goal_list]
#api_view(['GET'])
def get_initial_posts(request, count):
join_goal_list = get_current_user_join_goal_follow(request)
followee_list = get_current_user_followee(request)
serializer = full_post_data_serializer(
Post.objects.filter(join_goal__in=join_goal_list, creator__in=followee_list).order_by('-uuid')[:count])
return Response(serializer.data, status=status.HTTP_200_OK)
model.py
class Post(models.Model):
# ulid does ordered uuid creation
uuid = models.UUIDField(primary_key=True, default=generate_ulid_as_uuid, editable=False)
created = models.DateTimeField('Created at', auto_now_add=True)
updated_at = models.DateTimeField('Last updated at', auto_now=True, blank=True, null=True)
creator = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="post_creator")
join_goal = models.ForeignKey(JoinGoal, on_delete=models.CASCADE)
body = models.CharField(max_length=511, validators=[MinLengthValidator(5)])
hash_tags = models.ManyToManyField(HashTag)
type = models.CharField(
choices=PostType.choices,
max_length=2,
)
class FollowGoal(AbstractSimpleModel):
follower = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='goal_follow')
join_goal = models.ForeignKey(JoinGoal, on_delete=models.CASCADE)
# No update added, because cannot be edited. Can only be added and deleted
class FollowUser(AbstractSimpleModel):
follower = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='follower_id')
followee = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='followee_id')
# No update added, because cannot be edited. Can only be added and deleted
class User(AbstractDatesModel):
uuid = models.UUIDField(primary_key=True)
username = models.CharField(max_length=255, unique=True, validators=[
MinLengthValidator(8)])
created = models.DateTimeField('Created at', auto_now_add=True)
updated_at = models.DateTimeField('Last updated at', auto_now=True, blank=True, null=True)
Post.objects.filter(join_goal__in=join_goal_list, creator__in=followee_list)
is kind of close, but it means BOTH join_goal in my_list AND creator in followees
to join with OR you need to use Q expressions
from django.db.models import Q
join_goal_query = Q(join_goal__in = join_goal_list)
follower_post_query = Q(creator__in = followee_list)
is_join_goal_OR_follower_post = join_goal_query | follower_post_query
Post.objects.filter(is_join_goal_OR_follower_post ).all()
So I have this Cass_school model in which i have the many to many students field. What I want to do is to find the Class_school where request.user is in students. Any ideas?
class User(AbstractUser):
username = None
first_name = models.CharField(max_length=50, blank=False)
last_name = models.CharField(max_length=50, blank=False)
email = models.EmailField(('email address'), unique=True)
role = models.CharField(max_length=2, blank=False)
child = models.ForeignKey("User", on_delete=models.CASCADE, related_name="parent", blank=True, null=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ('first_name', 'last_name', 'role')
class Class_school(models.Model):
title = models.CharField(max_length=100)
principal_teacher = models.ForeignKey("User", on_delete=models.CASCADE, related_name="class_teacher")
students = models.ManyToManyField("User", related_name="class_students")
Also does anybody know if I can put an attribute to the students field so that a user can t be in two or more classes? Thank you!
UPDATE:
The python code below is used to get the subjects where origin-class is a foreign key for Class_school in order to get all of the subjects o f the class where the user in I need to get the Class_school first:
def classes_view(request):
schclass = Class_school.objects.filter(students=request.user)
subjects = Subject.objects.filter(origin_class=schclass)
subjects = subjects.order_by('title').all()
return render(request, "eduon/index.html", {
'subjects': subjects,
})
You can filter with:
Class_school.objects.filter(students=request.user)
Django will make a LEFT OUTER JOIN, and filter the records where request.user appears at the joined students relation.
There can be multiple of such Class_schools, since in a ManyToManyField, the same User can be a member of multiple Class_schools.
You can however not .filter(…) directly with this QuerySet, since it is a collection of Class_schools, not a single one. You can filter on the related object:
def classes_view(request):
subjects = Subject.objects.filter(
origin_class__students=request.user
).order_by('title')
return render(request, 'eduon/index.html', {
'subjects': subjects,
})
or use an __in lookup [Django-doc]:
def classes_view(request):
schclass = Class_school.objects.filter(students=request.user)
subjects = Subject.objects.filter(
origin_class__in=schclass
).order_by('title')
return render(request, 'eduon/index.html', {
'subjects': subjects,
})
Note: Models in Django are written in PerlCase, not snake_case,
so you might want to rename the model from Class_school to ClassSchool.