Repeating record using for loop in database - python

in this code am trying to add the student record in the attendance database whenever the student image is captured by the webcam so name = image name stored adding that the image name is the same as the studentid stored in Student entity, whenever the detected face name exist in Name = the list of images name then it will add the student info to the attendance database.
The code just works fine but it keeps repeating the records how can I limit it to add the record just once and not repeating it.
def markattendance(name):
for n in Names:
if name in Names:
# print(name, "Exist")
# fitches the information related to the detected name
attend = Student.objects.filter(student_id=name).values('student_id', 'student_fname', 'student_lname','fk_course_id_id')
#print(attend)
# filter returns a queryset. A queryset isn't a single object, it's a group of objects so it doesn't make sense
# to call save() on a queryset. Instead you save each individual object IN the queryset:
for object in attend:
#print(object)
if object.get('student_id') not in attend:
# INSERT SQL statement behind the scenes. Django doesn’t hit the database until you explicitly call save().
reg = Attendance(student_id=object.get('student_id'),
student_fname=object.get('student_fname'),
student_lname=object.get('student_lname'),
course_id=object.get('fk_course_id_id'))
# print(reg)
reg.save()
else:
pass

I'll try to answer with my understanding of your code (which may not be perfect, correct me if I'm mistaken).
First, your for loop at the start is useless, you don't use even once the n variable you introduce there. That's probably from where the problem is coming from, as you're executing again and again the same code.
Now, your attend queryset probably contains one Student instance as you're filtering by id, which I guess is unique among all Students. You then shouldn't be looping over each of its elements, as there's only one.
Finally, your line if object.get('student_id') not in attend: will always return False. You're getting the value of student_id of the unique element in your queryset, then looking if your queryset contains this same value.
But your queryset contains Student instances, not ids so it won't return True.
This, added to the fact that you're looping over these few lines of code, will result in you getting multiple records.
You probably need something like that (based on what I understood) :
def markattendance(name):
if name in Names:
attend = Student.objects.filter(student_id=name).values('student_id', 'student_fname', 'student_lname','fk_course_id_id')
student_with_name = attend[0]
reg = Attendance(student_id=object.get('student_id'),
student_fname=object.get('student_fname'),
student_lname=object.get('student_lname'),
course_id=object.get('fk_course_id_id'))
reg.save()

Related

Django Database querying

I'm having an issue here that I want clarification with, see, I'm making a program that does analysis of data.
I wanted to query data from different users, the data is numerical by the way, whenever I'd get subject marks from a user, I want the system to return the name of the user who has those marks.
It was all working fine until I tried querying the users with the same marks, and all I could get was an error
analyzer.models.ResultsALevel.MultipleObjectsReturned: get() returned more than one
ResultsALevel -- it returned 4!
So I was trying to find a way to still query and return the name of users with that subject mark, even if they have the same marks.
I believe that should be possible since the users have different id's and stuff, help would be much appreciated!
Here's my views.py
biology_marks = []
for student in ResultsALevel.objects.all():
biology_marks.append(student.advanced_biology_1)
value_1_biology =
ResultsALevel.objects.get(advanced_biology_1=biology_marks[-1]).student_name.last_name
value_2_biology =
ResultsALevel.objects.get(advanced_biology_1=biology_marks[-2]).student_name.last_name
value_3_biology =
ResultsALevel.objects.get(advanced_biology_1=biology_marks[-3]).student_name.last_name
value_4_biology =
ResultsALevel.objects.get(advanced_biology_1=biology_marks[-4]).student_name.last_name
ObjectManager.get() is used to retrieve single instances, usually selected by a primary key field.
Using ObjectManager.get() on a field where multiple data exists which matches the query, an error is returned (MultipeObjectsReturned)
Use ObjectManager.filter() in stead.

Fetch related ID's and aggregate in a new column

So my problem is coming from a poor understanding of the complexity of my query. A bit of a background story to this ask.
It's a car rental and search website, which started as a personal project of mine. I am using Django 2.1 for this, as well as Postgres.
The setup is as follows: I have a car model, which has an ID, a category, a car type, an engine etc. Secondly, there is an address table, that I use for all sorts of things.
What I would like to do now is the following:
I want to create Google Ads specific .csv files. This file needs a specific column with aggregated integers, in order to show 'related content' for the user. Meaning: You have seen Car A, here is a selection of related or similar cars in that area: Car K, O and Q.
I don't really have a problem making a csv from my data, but my problem is rather in constructing the query for this to work. I have the following as a first step:
cars = Car.objects
.select_all_related()
.only(
'id',
'name',
'address__city',
'address__city_area',
'images'
)
1
select_all_related joins the address table, because that's where the car is located. It also makes my only() call work, since I want to pull out specific fields
Select Related Doc Reference
2
only gives me only the fields I want, since I don't want to sent the whole model anyway, this also works.
Only Doc Reference
So selecting and getting the correct data is not the problem, but:
The real problem:
The following code should create a column in the table. This column should have aggregated IDs of the cars that are in a similar area (city, and city area). And this is sadly a requirement by the Google Ads thing I use.
def find_similiar_cars_in_same_city(cars: QuerySet):
"""Annotate the QuerySet with a field called similar_cars_ids containing
a list of ad IDs or None if there are none."""
similar_cars_queryset = Cars.objects.filter(
address__city=OuterRef('address__city'),
address__city_area=OuterRef('address__city_area'),
).exclude(id=OuterRef('id')).values_list(ArrayAgg('id'), flat=True)
# Hack: Remove GROUP BY that Django adds when adding ArrayAgg.
similar_cars_queryset.query.group_by = []
cars = cars.annotate(similar_cars_ids=Subquery(
similar_cars_queryset,
output_field=ArrayField(models.IntegerField())
))
return cars
And this kinda works. just takes forever. You also can see the comment I made in the code, that annotate() actually groups by which I don't really want here. I run everything locally and even just having 10 cars takes about 12 seconds. I'm unsure if im missing anything. It kinda works, but wont work for larger sample size. I ran it against a DB with roughly 14k cars and it just never finished.
So to summarize: I want to make a function that creates a column in the db with aggregated IDs of similar cars.
Anyone has a pointer on where to make this more efficient? And please ask if there are any more questions and I forgot to mention something!
Unless you're paginating over the results, it might be easier to handle it in python.
cars = Car.objects
.select_all_related()
.only(
'id',
'name',
'address__city',
'address__city_area',
'images'
)
cars_in_area_map = defaultdict(set)
for car in cars:
cars_in_area_map[(car.address.city, car.address.city_area)].add(car.id)
# Generate csv:
data = [
car.id,
car.name,
car.address.city,
car.address.city_area,
car.image,
{id for id in cars_in_area_map[(car.address.city, car.address.city_area)] if id != car.id},
]
If you need to paginate over them, you could try doing it via address:
data = []
addresses = Address.objects.prefetch_related('car_set')
for address in addresses:
cars = list(address.car_set.all())
for car in cars:
data.append([
car.id,
car.name,
address.city,
address.city_area,
car.image,
{c.id for c in cars if c.id != c},
])

Python/Django - How to annotate a QuerySet with a value determined by another query

Django 1.11, python 2.7, postgresql
I have a set of models that look like this:
class Book(Model):
released_at=DateTimeField()
class BookPrice(Model):
price = DecimalField()
created_at = DateTimeField()
Assuming multiple entries for Book and BookPrice (created at different points in time), I want to get a QuerySet of Book annotated with the BookPrice.price value that was current at the time the Book was released. Something like:
books = Book.objects.annotate(
old_price=Subquery(BookPrice.objects.filter(
created_at__lt=OuterRef('released_at')
)
.order_by('created_at')
.last()
.price
)
)
When I try something like this, I get an error: This queryset contains a reference to an outer query and may only be used in a subquery.
I could get the data with a for loop easily enough, but I'm trying to prepare a large chunk of data for a CSV download and I don't want to iterate through every book if I can help it.
Your problem is that you are doing .last().price. This code resolves (executes) the query and tries to get a python object. Hence the error you are getting as the query you are trying to execute contains an OuterRef, therefore it cannot be executed.
You should transform your query into something like the following:
last_price_before_release_query = BookPrice.objects.filter(created_at__lt=OuterRef('released_at')).order_by('-created_at').values('price') # Note the reversed ordering
books = Book.objects.annotate(old_price=Subquery(last_price_before_release_query[:1]))
You can get more information here.

Extend queryset in django python

I am looking for way how to add new objects to existing queryset, or how to implement what I want by other way.
contact = watson.filter(contacts, searchline)
This line returns queryset, which I later use to iterate.
Then I want to do this to add more objects, which watson couldn't find
contact_in_iteration = Contact.objects.get(id = fild.f_for)
contact.append(contact_in_iteration)
And sorry for my poor english
Did this
contacts = Contact.objects.filter(crm_id=request.session['crm_id'])
query = Q(contacts,searchline)
contact = watson.filter(query)
and get 'filter() missing 1 required positional argument: 'search_text'' error
You can use | and Q lookups. See the docs.
I'm not sure I've fully understood your initial query, but I think that in your case you would want to do:
query = Q(contacts='Foo', searchline='Bar')
contact = watson.filter(query)
Then later:
contact = watson.filter(query | Q(id=field.f_for))
Strictly speaking it won't append to the queryset, but will return a new queryset. But that's okay, because that's what .filter() does anyway.
You should look at a queryset as a sql query that will be executed later. When constructing a queryset and save the result in a variable, you can later filter it even more, but you can not expand it. If you need a query that has more particular rules (like, you need an OR operation) you should state that when you are constructing the query. One way of doing that is indeed using the Q object.
But it looks like you are confused about what querysets really are and how they are used. First of all:
Contact.objects.get(id = fild.f_for)
will never return a queryset, but an instance, because you use get and thus ask for a single particular record. You need to use filter() if you want to get a quersyet. So if you had an existing queryset say active_contacts and you wanted to filter it down so you only get the contacts that have a first_name of 'John' you would do:
active_contacts = Contact.objects.filter(active=True)
active_contacts_named_John = active_contacts.filter(first_name='John')
Of course you could do this in one line too, but I'm assuming you do the first queryset construction somewhere else in your code.
Second remark:
If in your example watson is a queryset, your user of filter() is unclear. This doesn't really make sense:
contact = watson.filter(contacts, searchline)
As stated earlier, filtering a queryset returns another queryset. So you should use a plurar as your variable name e.g. contacts. Then the correct use of filter would be:
contacts = watson.filter(first_name=searchline)
I'm assuming searchline here is a variable that contains a user inputted search term. So maybe here you should name your variable searchterm or similar. This will return all contacts that are filtered by whatever watson is filtering out already and whose first_name matches searchline exactly. You could also use a more liberate method and filter out results that 'contains' the searching term, like so:
contacts = watson.filter(first_name__contains=searchline)
Hope this helps you get on the right path.

QuerySet optimization

I need to find a match between a serial number and a list of objects, each of them having a serial number :
models:
class Beacon(models.Model):
serial = models.CharField(max_length=32, default='0')
First I wrote:
for b in Beacon.objects.all():
if b.serial == tmp_serial:
# do something
break
Then I did one step ahead:
b_queryset = Beacon.objects.all().filter(serial=tmp_serial)
if b_queryset.exists():
#do something
Now, is there a second step for more optimization?
I don't think it would be faster to cast my QuerySet in a List and do a list.index('tmp_serial').
If your serial is unique, you can do:
# return a single instance from db
match = Beacon.objects.get(serial=tmp_serial)
If you have multiple objects to get with the same serial and plan do something on each of them, exist will add a useless query.
Instead, you should do:
matches = Beacon.objects.filter(serial=tmp_serial)
if len(matches) > 0:
for match in matches:
# do something
The trick here is that len(matches) will force the evaluation of the queryset (so your db will be queried). After that,
model instances are retrieved and you can use them without another query.
However, when you use queryset.exists(), the ORM run a really simple query to check if the queryset would have returned any element.
Then, if you iterate over your queryset, you run another query to grab your objects. See the related documentation for more details.
To sum it up: use exists only if you want to check that a queryset return a result a result or not. If you actually need the queryset data, use len().
I think you are at best but if you just want whether object exists or not then,
From django queryset exists()
https://docs.djangoproject.com/en/1.8/ref/models/querysets/#django.db.models.query.QuerySet.exists
if Beacon.objects.all().filter(serial=tmp_serial).exists():
# do something

Categories

Resources