Iterating Through a Database Django - python

I have a model in my database called Item. This is the model
class Item(models.Model):
item_title = models.CharField("Item title: ", max_length=50)
item_description = models.CharField("Item description: ", max_length=200)
starting_price = models.FloatField( default = 0)
picture = models.ImageField(upload_to = 'gallery')
finishTime = models.DateTimeField("Finish time: ")
ownerID = models.ForeignKey(User, on_delete = models.CASCADE)
higestBid = models.FloatField()
highestBidderID = models.IntegerField(blank=True, default=-1)
active = models.BooleanField(default = True)
I want to create a function in views.py , which will check the 'active' field of each item in the database. How can I do that? I have tried to do this:
items = list(Item.object.values())
for i in range(items):
print(item[i].active)
However it does not work. Any help is appreciated

You should iterate over the queryset itself:
for item in Item.objects.all():
print(item.active)
Please do not use .values() [Django-doc]. You should only use that for some specific cases, like grouping on a certain value. By working with objects, you will still use model objects, and thus certain logic you added on that object, is still accessible.
Furthermore it is often better not to use list(..). A QuerySet is lazy, and thus if you somehow never iterate over it, then it will not make the query. Furthermore you can then do extra filtering on an existing QuerySet.

Related

Django get rid of duplicated queries in nested models

I have models as shown below,
class Manufacturer(models.Model):
name = models.CharField(max_length=100)
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
name = models.CharField(max_length=300)
#property
def latest_variant(self):
return self.carvariant_set.last()
class CarVariant(models.Model):
car = models.ForeignKey(Car, on_delete=models.CASCADE)
name = models.CharField(max_length=300)
and I am making a query to get the latest variant of all cars, I am getting much duplicated queries.
I couldn't eliminate it with prefetch_related
Car.objects.all().prefetch_related('carvariant_set')
How can I eliminate the duplicated queries?
If you use .prefetch_related it will populate the carvariant_set value, but only for a .all() query, not for a .last(), that will trigger a new query.
What we can do is define a property like:
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
name = models.CharField(max_length=300)
#property
def latest_variant(self):
items = getattr(self, '_latest_variants', ())
if items:
return items[-1]
return self.carvariant_set.last()
Then we can prefetch the related object with:
from django.db.models import Prefetch
Car.objects.prefetch_related(
Prefetch(
'carvariant_set',
queryset=CarVariant.objects.order_by('pk'),
to_attr='_latest_variants'
)
)
To get rid of duplicates you use "distinct()".
For example Car.objects.all().prefetch_related('carvariant_set').distinct(). You can read about it here:
https://docs.djangoproject.com/en/3.2/ref/models/querysets/#django.db.models.query.QuerySet.distinct
sometimes you might need to tell the "distinct" function which fields make an object distinct. By default it's the id, but you can do something like "distinct('name')" in order to avoid getting 2 instances with the same name for example.

How to iterate through all foreign keys pointed at an object in Django without using _set notation?

I am fairly new to Django, but I am working on an application that will follow a CPQ flow or Configure, Price, Quote. The user should select the product they would like to configure as well as the options to go with it. Once selected, the program should query an external pricing database to calculate price. From there the program should output the pricing & text data onto a PDF quote. I was able to get the application working using the specific product inheriting from a base product class. The issue is now that I've created a second product child class, I cannot use a singular "related_name". I've omitted the lists associated with the drop down fields to help with readability, but I've posted my models.py file below.
Is there a way I can iterate through Product objects that are pointing to a Quote object with a foreign key? A lot of answers I've found on SO relating to this were able to be solved either by specifying the "_set" or "related_name". I've seen other answers use the select_related() method, however, I can't seem to get the query right as the program won't know which set it needs to look at. A quote could have any mix of product instances tied to it, so am unsure how to handle that query. Again, I have been using django under 6 months, so I am a bit green. I am not sure if I am just not fundamentally understanding the big picture here. I thought about instead of using inheritance, to make Product a standalone class and to save the Compact or WRC info to it so I could just use one "related_name", but also thought that would just create another nested layer that would still fail.
Any help would be very appreciated! I've definitely hit the wall.
models.py
class Quote(models.Model):
project_name = models.CharField(max_length=256,blank=True)
customer_first_name = models.CharField(max_length=256,blank=True)
customer_last_name = models.CharField(max_length=256,blank=True)
company_name = models.CharField(max_length=256, blank=True)
address1 = models.CharField(max_length=256, blank=True, help_text ="Address")
address2 = models.CharField(max_length=256, blank=True)
city = models.CharField(max_length=256, blank=True, default="")
state = models.CharField(max_length=256, blank=True, default="")
zip_code = models.CharField(max_length=256, blank=True, default="")
country = models.CharField(max_length=256, blank=True, default="")
phone = PhoneField(blank=True)
email = models.EmailField(max_length=254,blank=True)
grand_total = models.FloatField(default=0)
create_date = models.DateTimeField(default = timezone.now)
class Product(models.Model):
class Meta:
abstract = True
price = models.FloatField(default=0)
total_price = models.FloatField(default=0)
quantity = models.IntegerField()
quote = models.ForeignKey('quote.Quote', on_delete=models.CASCADE)
quantity = models.IntegerField()
class Compact(Product):
base_size = models.CharField(choices=size, max_length = 256)
filter = models.CharField(choices=filter_list, max_length = 256)
product_name = models.CharField(max_length=256,default="Compact")
class WRC(Product):
base_size = models.CharField(choices=size, max_length = 256)
construction = models.CharField(choices=construction_list, max_length = 256)
outlet = models.CharField(choices=outlet_list, max_length = 256)
product_name = models.CharField(max_length=256,default="WRC")
I was able to figure out my issue, but wanted to answer in case someone came across a similar problem as myself. I was able to get get all product objects attached to a quote instance dynamically by modifying the get_context_data() method of my QuoteDetailView. I also needed to use the django library NestedObjects from django.contrib.admin.utils to grab all related objects to the quote instance. I also added a timestamp field to the Product class to be able to sort them. QuoteDetailView copied below.
class QuoteDetailView(FormMixin,DetailView):
model = Quote
form_class = ProductSelectForm
def get_context_data(self, **kwargs):
### collects related objects from quote
collector = NestedObjects(using=DEFAULT_DB_ALIAS)
collector.collect([kwargs['object']])
### slice off first element which is the quote itself
related_objects = collector.nested()
related_objects = related_objects[1:]
### get context data for qoute object
context = super().get_context_data(**kwargs)
context['now'] = timezone.now()
### if number of list items is above 0, then add them to the context
### and sort by timestamp
if len(related_objects) != 0:
context['items'] = sorted(related_objects[0], key=lambda x: x.timestamp)
return context

Why Django DoesNotExist Erase other objects?

I'm facing an issue with Django and specially the use of DoesNotExist.
In my script, I'm trying to check if 'Scraper' object exist or not, if it doesn't the object is created.
For checking if it exist or not i'm using a 'try catch Model.DoesNotExists', .get() with two parameters the first one is an IDs and the last the User object.
The issue is rising when two Scraper object has a same ids but different user's, the first one 'Scraper' is created and the second erase the first one.
try:
a = Scraper.objects.get(id_lbc=ids, user=user)
except Scraper.DoesNotExist:
a = Scraper(
id_lbc=ids,
lbc_name=title,
lbc_url=url,
lbc_image=image,
lbc_price=price,
lbc_date=date,
lbc_city=city,
lbc_price_meter=price_meter,
user=user,
token_url=token,
customer=customer,
has_phone=phone,
)
a.save()
Model
class Scraper(models.Model):
id_lbc = models.IntegerField(primary_key=True)
lbc_name = models.CharField(max_length=300)
lbc_url = models.URLField(max_length=300)
lbc_image = models.URLField(
max_length=300, default='http://****/****/nothing.jpg')
lbc_price = models.IntegerField(blank=True, null=True)
lbc_price_meter = models.IntegerField(blank=True, null=True)
lbc_city = models.CharField(max_length=300)
lbc_date = models.DateTimeField(auto_now=False)
user = models.ForeignKey(User, on_delete=models.CASCADE)
token_url = models.ForeignKey(
Url_lbc, to_field='token', on_delete=models.CASCADE)
is_emailed = models.BooleanField(default=False)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
has_phone = models.BooleanField(default=False)
def __str__(self):
return self.lbc_name
What I want is create two Scraper object with same 'ids' but different User's
for example
Scraper N°1
ids = 1234567
user = Nico(object)
Scraper N°2
ids = 1234567
user = Paul(object)
I thought with the User object given, the Django query can see the difference between two Scraper object but I misunderstood something...maybe the PK in my model ?
Thanks for the help
You can't do what you want with your current model design. This is because id_lbc is set as a primary key. This means it has to be unique, you can't have two istances sharing that value. What you could do though is:
class Scraper(models.Model):
id_lbc = models.IntegerField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
...
class Meta:
unique_together = [('user', 'id_lbc')]
This will make it such that id_lbc can have duplicates within the table as long as each value has a different user value.
Then you should also use Scraper.objects.get_or_create:
a, created = Scraper.objects.get_or_create(id_lbc=ids, user=user, defaults={
'lbc_name': title,
'lbc_url': url,
'lbc_image': image,
'lbc_price': price,
'lbc_date': date,
'lbc_city': city,
'lbc_price_meter': price_meter,
'token_url': token,
'customer': customer,
'has_phone': phone,
})
Do not use the Scraper directly to create the object, call instead Scraper.objects.create(...)

Django query selecting values and objects

I have a problem with the queries when selecting values and objects. Here is a sample structure:
class Property(models.Model):
name = models.CharField(max_length=70, blank=True, verbose_name="Property Name")
type = models.CharField(max_length=10)
class Agreement(models.Model):
property = models.ForeignKey(Property, on_delete=models.CASCADE, related_name="prop")
renter = models.ForeignKey(User, verbose_name="Kiracı", related_name = "renter01")
Here is the first filter.
qs1 = Agreement.objects.all()
This one returns property and renter as objects. So I can refer the object details such as
for q in qs:
print(q.renter.firstname)
Here is the second filter.
When I need only some fields I use this filter:
qs2 = Agreement.objects.all().values('renter',...)
In that case the query returns the pk value of the renter user; and I cannot use it as object.
Is there a way that I can select certain columns and keep the objects in it as objects?
If you want renters, you should query User, not Agreement.
renters = User.objects.exclude(renter01=None)
(Note, having renter01 as the reverse relation makes no sense; unless you have a good reason, you should keep it as the default, which is agreement_set.)

How to add queryset to ManyToMany relationship?

I have following models:
class EnMovielist(models.Model):
content_ID = models.CharField(max_length=30)
release_date = models.CharField(max_length=30)
running_time = models.CharField(max_length=10)
actress = models.CharField(max_length=300)
series = models.CharField(max_length=30)
studio = models.CharField(max_length=30, null=True)
director = models.CharField(max_length=30)
def __str__(self):
return self.content_ID
class EnActress(models.Model):
name = models.CharField(max_length=100, null=True)
movielist = models.ManyToManyField(EnMovielist, related_name='movies')
def __str__(self):
return self.name
I got error when I try to this in Django shell,
b = EnActress.objects.values_list('name', flat=True)
a = EnMovielist.objects.filter(actress__contains=b).values_list('content_ID')
b.movielist.add(a)
AttributeError: 'QuerySet' object has no attribute 'movielist'
How can I django queryset add into many-to-many field?
I have no idea why this is happening.. Any help appreciated! :)
You should not be using values_list if you intend to add a new relation afterwards. From the docs:
values() and values_list() are both intended as optimizations for
a specific use case: retrieving a subset of data without the
overhead of creating a model instance
[Emphasis mine]
It's hard to tell what you're up to without having a good description of what you want to achieve.
You should call m2m add from instance and adding entity should be also model instance. Otherwise your expression doesn't make sense.
b = EnActress.objects.get(pk=some_pk) # get an instance, not queryset
a = EnMovielist.objects.get(pk=some_pk) # also instance
b.movielist.add(a)

Categories

Resources