Learning Django by creating an eBay like bidding application.
One of my models is a simple bid class that will record every user's bid for a particular listing.
models.py
class Bid(models.Model):
bid = models.DecimalField(max_digits=10, decimal_places=2)
user = models.ForeignKey(User, on_delete=models.CASCADE)
listing = models.ForeignKey(Listing, on_delete=models.CASCADE)
forms.py
def listing_view(request, id):
form = BidForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.user = request.user
instance.listing_id = id
# check bid is higher than starting bid or other bids or flash error message
current_bid = instance.bid
starting_bid = instance.listing.starting_bid
query = Bid.objects.all().filter(listing=id)
In forms.py, I am writing a view to validate that the bid the user entered is larger than existing bids. It's incomplete because I am stuck on creating this comparison.
The easy way is to loop over the 'query' variable and do the comparison, but is there a more elegant solution?
I found a solution using the all() function, something like:
all(current_bid > i in my_list)
But this only works for comparing against a list, not form objects
Is there a way to loop over query (i.e. for each in query) and get check whether current_bid is greater than all of the 'each.bid' in 1 line?
Something like this:
all(current_bid > i for i.bid in query)
Sadly, this doesn't work. I get a NameError - name 'i' is not defined.
Thanks!
This line here:
all(current_bid > i for i.bid in query)
needs to be changed to something like this:
all(current_bid > i.bid for i in query)
Sometimes list comprehensions are confusing so I like to imagine them as normal for loops. Here's my interpretation of what you're trying to do (no guarantees of correctness):
is_biggest_bid = True
for i in query:. # writing i.bid wouldn't make sense here
if i.bid > current_bid:
is_biggest_bid = False
break
Hope that helps.
Related
I am learning Django. I wrote a simple model and some views method in Django rest framework so that I can modify some particular attributes when needed to all the records that need that. Here is the model:
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=255)
isActive = models.BooleanField(default=True)
def __str__(self):
return self.name
Then, I created this view to modify the isActive session when I call it:
class CategoriesChangeActiveView(views.APIView):
def post(self, request, format=None):
try:
categories = request.data.get('categories')
for category in categories:
category = Category.objects.get(id=category['id'])
category.isActive = category['isActive']
category.save()
except Exception as e:
return Response({'error': 'Bad request'}, status=status.HTTP_400_BAD_REQUEST)
return Response({'success': 'Active changed'}, status=status.HTTP_200_OK)
Even when the format of my request is correct ( I debugged each line ) when it comes to the line category.isActive = category['isActive']it throws the error that'Category' object is not subscriptable`. I don't know why or how to fix it.
I saw in the official documentation, on older StackOverflow questions that this is doable, but I don't understand why I can't.
Can someone please suggest what I am doing wrong? Thank you
it's a simple mistake.
Simply change it as follows and it should be fixed:
categories = request.data.get('categories')
for category in categories:
category_obj = Category.objects.get(id=category['id'])
category_obj.isActive = category['isActive']
category_obj.save()
What you're doing is changing what the variable category is. You for loop and the unpacked variable is category, but then you get the model object and set the variable as category
So initially, the category variable is in fact a dictionary object, but you change it to be a django model object instance.
Specifically, the issue is here:
category = Category.objects.get(id=category['id'])
category.isActive = category['isActive']
You set category to be an instance of the Category model (which in this case corresponds to a db record, but that bit is a little irrelevant).
Accessing attributes on a class instance is not done by the square bracket notation, but rather dot notation.
So instead of category['isActive'] use category.isActive
If category was a dictionary, eg.
category = {
"name": "cat",
"isActive": True,
}
Then you would use the square bracket notation as category["isActive"] to get that value.
As it is, it's not a dict, so python thinks you are trying to subscript the instance somehow, which will not work.
Hello django developers !. I hope you all are fine.. :)
Well here i want to get the shop_owner from shop detail view to the get context data function so i can count the total products of that particular shop..
models.py
class ShopProfile(models.Model):
shop_owner = models.OneToOneField(User, related_name='shop_profile', on_delete=models.CASCADE)
shop_address = models.CharField(_("shop address"), max_length=255)
views.py
class ShopProfileDetailView(DetailView):
model = ShopProfile
template_name='shops/shop_profile.html'
def get_context_data(self,*args, **kwargs):
context = super(ShopProfileDetailView, self).get_context_data(*args, **kwargs)
user = context['shop_owner'] #getting error here
context["products_count"] = Product.objects.filter(product_owner=user).count()
return context
If the KeyError is raised from a failed dictionary key lookup in your own code, you can use . get() to return either the value found at the specified key or a default value.
Well I fixed it, By printing the context variable above the error line, in my case
print('context data',context)
user = context['shop_owner'] #getting error here
It will return some fields in the terminal then You can simply get any data from that context. :)
I've been working on Django for a while and now I came across this strange problem.
I'm adding a rating feature on a streaming website, for which I wrote a view that is called through a url:
url(r'^channel/rate/(?P<stream_id>[\d]*)/(?P<rate>[\d]*)/?$', 'eros.streaming.views.view_rate_channel',
name='view_rate_channel'),
def view_rate_channel(request,stream_id,rate):
if(stream_id and rate and request.user.is_authenticated()):
stream= Stream.objects.get(id=stream_id)
if stream not in request.user.channel.rated_streams.all():
if stream.channel.user != request.user:
channel=Channel.objects.get(stream=stream)
channel.rating= channel.rating+int(rate)
print("channel rating: "+str(channel.rating))
#this prints fine in any case
channel.n_voters = channel.n_voters +1
channel.save()
stream.rating= stream.rating+int(rate)
stream.n_voters= stream.n_voters+1
stream.save()
request.user.channel.rated_streams.add(stream)
request.user.channel.save()
return HttpResponseRedirect('/channel/'+str(stream.channel.id)+'/')
return HttpResponseRedirect('/channel/'+str(stream.channel.id)+'/')
When I check on the database, the changes I made on the Channel object after channel.save() are not saved, only the changes on the Stream object which I found weird.
So after doing some debugging I decided to comment this if, which I use so users can't rate a stream more than once:
##if stream not in request.user.channel.rated_streams.all():
And now the channel.save() is working!, the bad thing is that I can't let users to rate a stream more than once.
Heres a simplified version of the models:
class Channel(models.Model):
user = models.OneToOneField(User)
rating = models.IntegerField(default=0)
n_voters = models.IntegerField(default=0)
rated_streams = models.ManyToManyField('Stream', related_name="rated_streams")
description = models.TextField(default="")
class Stream(models.Model):
## I think that maybe this relation is what is causing me trouble (?):
channel = models.ForeignKey(Channel)
rating = models.IntegerField(default=0)
n_voters = models.IntegerField(default=0)
Is there any bad practice or bad query that I'm doing that is making this happen? Thanks a lot in advance.
Using channel = stream.channel instead of channel = Channel.objects.get(stream=stream) fixed this.
Suppose I got two models like this:
class Article(models.Model):
article_title = models.CharField(max_length=100)
class EventRecord(models.Model):
article = models.ForeignKey(Article)
In a view, I select a certain EventRecord and want to show the Title of the Article it is related to as well. The following does not work:
def classify(request, pk):
event = get_object_or_404(EventRecord, pk=pk)
article_id = event.article
article = get_object_or_404(Article, pk=article_id)
How do I make this work?
Any help is really appreciated!
Django automatically handles this for you. For example:
>>> record = EventRecord.objects.get(...)
>>> isinstance(record.article, Article)
True
>>> record.article.article_title
u'title here'
This is one of the magical things Django does (nothing is magic but anyway...). Please keep in mind that in order for this work Django will usually execute some extra database queries. To eliminate them, you can use select_related method. Below is a snippet which eliminates extra queries and does what you want:
def classify(request, pk):
record = EventRecord.objects.filter(pk=pk).select_related()
# the above returns queryset hence you have to extract the record manually
if not len(record):
raise Http404()
else:
record = record[0]
# now use record as usual and no extra queries will be executed
title = record.article.article_title
...
event.article returns the actual Article object, not the primary key, so you don't need to do another database query.
def classify(request, pk):
event = get_object_or_404(EventRecord, pk=pk)
if not event.article:
raise Http404
print event.article.article_title
I have a database table. Some database items can be edited by a user, but only one user can edit the table content at a time, and if after 2 hours the user hasn't finished editing, other users can edit the table. How can I do this?
The table is like this:
class NodeRevision(BaseModel, NodeContent):
node = models.ForeignKey(Node, related_name='revisions')
summary = models.CharField(max_length=300)
revision = models.PositiveIntegerField()
revised_at = models.DateTimeField(default=datetime.datetime.now)
suggested = models.BooleanField(default=False)
suggest_status = models.CharField(max_length=16,default="")
Should I add a BooleanField to it, such as editing_locked=models.BooleanField(default=False) ? Or something else? And how could I implement the 2 hour check?
You'd need a locked_at time field and locked_by field.
Every time somebody loads an edit page, update the database with the locked_at and locked_by information.
To implement the 2 hour restriction, I'd just have the results calculated only when a user asks for permission (as opposed to polling / updating models). When a user tries to edit a model, have it check locked_by/locked_at and return a Boolean whether it's editable by the user or not.
def can_edit(self, user):
if user == self.locked_by:
return True
elif self.locked_at and (self.locked_at - datetime.datetime.now()).total_seconds > 2*60*60:
return True
return False