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. :)
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.
I have read several answers to this same question, but none have worked for me. Here's my model:
class States(models.Model):
name = models.CharField(max_length=20)
abrev = models.CharField(max_length=3)
def __str__(self):
return self.abrev
class Offers(models.Model):
*other fields*
state = models.ForeignKey(States on_delete=models.PROTECT)
so in views i am doing this:
if request.method == 'POST':
try:
**other fields**
state = States.objects.get(pk=request.POST['state'])
Offers.objects.create(**other fields**, state=state)
except Exception as e:
print ("error in form")
But am always getting the same Error
Cannot assign "'1'": "Offers.state must be a "States" instance.
And as you can see, i am passing an instance, not the id of the element. I use the id that comes from the form just to query the DB and find the instance with this: state = States.objects.get(pk=request.POST['state'])
The form works just fine, and I tried to do it with forms.py and if form.is_valid(), but got the exact same result.
am on:
django 3.2.3
python 3.9.2
EDIT**
It works in the django admin.
In Offers.objects.create(other fields, state=state)
Here other fields is dict type contains all values to the model.
In the dict add
other_fields["created_by"] = state
or
other_fields["created_by_id"] = state.id
Then create,
Offers.objects.create(**other_fields)
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
An odd error here, perhaps someone can help track down source as it's attempting to extend the Django CMS project & attempts to use uses some logic written as part of that project which I'm not fully clear on. In short, using:
urls.py
======================
from django.conf.urls.defaults import *
from cmsplugin_flat_news.models import News
'''RETURNING _CLONE ERROR WHEN IMPLEMENTED
def get_news():
return News.published.all()
news_dict = {
'queryset': get_news,
}
news_list_dic = {
'queryset': get_news,
'paginate_by': 50,
}
'''
# NEXT SECTION FUNCTIONS BUT NEEDS SERVER RESTART TO SEE NEW POSTS.
#CHANGING TO JUST News.published.all RAISES SAME ISSUE AS USING WRAPPER
#SOLUTION ABOVE. SEE: http://docs.djangoproject.com/en/dev/topics/db/queries/#caching-and-querysets
#& EXAMPLE HERE: http://docs.djangoproject.com/en/dev/topics/generic-views/#adding-extra-context
news_dict = {
'queryset': News.published.all(),
}
news_list_dic = {
'queryset': News.published.all(),#SAME ISSUE
'paginate_by': 50,
}
urlpatterns = patterns('django.views.generic.list_detail',
(r'^$', 'object_list', news_list_dic),
(r'^(?P<page>[0-9]+)/$', 'object_list', dict(news_list_dic)),
url(r'^(?P<slug>[-\w]+)/$', 'object_detail', news_dict, name='news_view'),
)
models.py
======================
class PublishedNewsManager(models.Manager):
#Filters out all unpublished news and news with a publication date in the future
def get_query_set(self):
return super(PublishedNewsManager, self).get_query_set() \
.filter(is_published=True) \
.filter(pub_date__lte=datetime.datetime.now())
class News(models.Model):
title = models.CharField(_('Title'), max_length=255)
slug = models.SlugField(_('Slug'), unique_for_date='pub_date')
author = models.ForeignKey(User)
description = models.TextField(_('Description'), blank=True)
image = generic.GenericRelation('NewsImage', blank=True, null=True)
content = models.TextField(_('Content'), blank=True)
tags = TagField()
is_published = models.BooleanField(_('Published'), default=False)
pub_date = models.DateTimeField(_('Publication date'), default=datetime.datetime.now())
created = models.DateTimeField(auto_now_add=True, editable=False)
updated = models.DateTimeField(auto_now=True, editable=False)
published = PublishedNewsManager()
objects = models.Manager()
See issue in comments: basically, error raised by implementing the 'correct' solution to add extra context to the views. Error is Attribute Error: "'function' object has no attribute '_clone'"
Trying: News.published.all instead of News.published.all() raises the error whether used as part of a wrapper function or directly in the queryset part of the urlpattern.
Must be missing something obvious? Think it is to do with the PublishedNewsManager not returning objects as a dictionary, or tweaking the code to correctly return the objects to the view.
The _clone errors are a red herring caused by you passing a function as an argument to a generic view where a QuerySet is expected. The version of your code which passes News.published.all() to the generic views is correct, as generic views will try to clone the QuerySet they are given, to avoid caching the first lot of data they query for (hence the error when you pass in a function).
Your problem seems to be that your custom get_query_set method is returning a QuerySet filtered based on the current date and time when the method was called.
I can't see anything in the documentation about filter arguments being callable, but I did find this ticket which suggests that filter can take callable arguments, so try changing your manager to pass in the function to be called to get the current date/time, rather than calling it immediately:
class PublishedNewsManager(models.Manager):
def get_query_set(self):
return super(PublishedNewsManager, self).get_query_set() \
.filter(is_published=True) \
.filter(pub_date__lte=datetime.datetime.now)
Well, I ain't exactly in the same use case as the OP, but I was trying to pass a RawQuerySet to the generic views and getting the _clone error, which I fixed with an ugly hack to the RawQuerySet instance.
It seems the only thing the generic view wants to do is to clone the queryset (for caching purposes?), so I had the object return a copy of itself which satisfied the call to _clone.
error:
'RawQuerySet' object has no attribute '_clone'
code:
from django.views.generic.list_detail import object_list
....
li_file = File.objects.raw("SELECT * FROM file_file WHERE fn like %s", [search])
#adding this fixed the _clone missing error
def clone(self):
#return self - this works as well.
return copy.copy(self)
li_file.__class__._clone = clone
#end of addition
return object_list(request,
queryset = li_file,
template_name = "file/found.html",
)
Here's a Django model class I wrote. This class gets a keyerror when I call get_object_or_404 from Django (I conceive that keyerror is raised due to no kwargs being passed to __init__ by the get function, arguments are all positional). Interestingly, it does not get an error when I call get_object_or_404 from console.
I wonder why, and if the below code is the correct way (ie, using init to populate the link field) to construct this class.
class Link(models.Model)
event_type = models.IntegerField(choices=EVENT_TYPES)
user = models.ForeignKey(User)
created_on = models.DateTimeField(auto_now_add = True)
link = models.CharField(max_length=30)
isActive = models.BooleanField(default=True)
def _generate_link(self):
prelink = str(self.user.id)+str(self.event_type)+str(self.created_on)
m = md5.new()
m.update(prelink)
return m.hexdigest()
def __init__(self, *args, **kwargs):
self.user = kwargs['user'].pop()
self.event_type = kwargs['event_type'].pop()
self.link = self._generate_link()
super(Link,self).__init__(*args,**kwargs)
self.user = kwargs['user'].pop()
self.event_type = kwargs['event_type'].pop()
You're trying to retrieve an entry from the dictionary, and then call its pop method. If you want to remove and return an object from a dictionary, call dict.pop():
self.user = kwargs.pop('user')
Of course, this will fail with a KeyError when "user" is not present in kwargs. You'll want to provide a default value to pop:
self.user = kwargs.pop('user', None)
This means "if "user" is in the dictionary, remove and return it. Otherwise, return None".
Regarding the other two lines:
self.link = self._generate_link()
super(Link,self).__init__(*args,**kwargs)
super().__init__() will set link to something, probably None. I would reverse the lines, to something like this:
super(Link,self).__init__(*args,**kwargs)
self.link = self._generate_link()
You might want to add a test before setting the link, to see if it already exists (if self.link is not None: ...). That way, links you pass into the constructor won't be overwritten.
There's no reason to write your own __init__ for Django model classes. I think you'll be a lot happier without it.
Almost anything you think you want to do in __init__ can be better done in save.
I don't think you need the __init__ here at all.
You are always calculating the value of link when the class is instantiated. This means you ignore whatever is stored in the database. Since this is the case, why bother with a model field at all? You would be better making link a property, with the getter using the code from _generate_link.
#property
def link(self):
....
wonder why, and if the below code is the correct way (ie, using __init__ to populate the link field) to construct this class.
I once got some problems when I tried to overload __init__
In the maillist i got this answer
It's best not to overload it with your own
__init__. A better option is to hook into the post_init signal with a
custom method and in that method do your process() and
make_thumbnail() calls.
In your case the post_init-signal should do the trick and implementing __init__ shouldn't be necessary at all.
You could write something like this:
class Link(models.Model)
event_type = models.IntegerField(choices=EVENT_TYPES)
user = models.ForeignKey(User)
created_on = models.DateTimeField(auto_now_add = True)
link = models.CharField(max_length=30)
isActive = models.BooleanField(default=True)
def create_link(self):
prelink = str(self.user.id)+str(self.event_type)+str(self.created_on)
m = md5.new()
m.update(prelink)
return m.hexdigest()
def post_link_init(sender, **kwargs):
kwargs['instance'].create_link()
post_init.connect(post_link_init, sender=Link)
>>> link = Link(event_type=1, user=aUser, created_on=datetime.now(), link='foo', isActive=True)
providing keyword unique for link = models.CharField(max_length=30, unique=True) could be helpful, too. If it is not provided, get_object_or_404 may won't work in case the same value in the link-field exists several times.
signals and unique in the django-docs