Having trouble with an UpdateView. I've tried over writing the get_object but I am getting
AttributeError at /companydata/update/
'User' object has no attribute 'get_companydata'
The CompanyData Model has a OneToOne relationship with User.
Here's my code:
urls.py
### Omitted ###
url(r'^update/$', CompanyDataUpdateView.as_view(),
name='companydataupdate')
### Omitted ###
views.py
class CompanyDataUpdateView(UpdateView):
model = CompanyData
fields = ['arr', 'num_cust']
template_name = 'company_data/companydata_form.html'
def get_object(self):
return self.request.user.get_companydata()
models.py
class CompanyData(models.Model):
user = models.OneToOneField(User)
arr = models.DecimalField(max_digits=20, decimal_places=2, validators=[MinValueValidator(1)])
num_cust = models.IntegerField(validators=[MinValueValidator(1)])
def get_absolute_url(self):
return reverse('companyrevenue')
Any help would be greatly apprecaited!
The User object has no method called get_companydata, hence your error. You need to access the reverse one-to-one relationship like so:
def get_object(self):
return self.request.user.companydata
Where companydata is a property, not a method (i.e., don't call it with brackets). This is the default reverse name for the one-to-one relationship:
If you do not specify the related_name argument for the OneToOneField, Django will use the lower-case name of the current model as default value.
If you want to be more explicit or use another name, then set the related_name on your OneToOneField.
Related
Hello Django Programmers,
I have an issue which I don't understand.
Here is my model class:
class Profile(models.Model):
name = models.CharField(max_length=50)
employeeView = models.BooleanField(default=True)
owner = models.OneToOneField(User, related_name="profile", on_delete=models.CASCADE, null=True)
This class extends my User Model. Please notice that I'm using here OneToOneField in relation to User Model.
This model is serialized with that Serializer:
class ProfileSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Profile
fields = (
'url',
'pk',
'name',
'employeeView')
And finally I have view which I use to process GET (list) and POST requests to the database:
below view works fine (for GET request), but it gives me all Profiles related with all users
class ProfileList(generics.ListCreateAPIView):
permission_classes = [
permissions.IsAuthenticated
]
serializer_class = ProfileSerializer
name = 'profile-list'
queryset = Profile.objects.all()
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
Because I would like to create view which could give me only profile for My user, I modifiet above view like so:
class ProfileList(generics.ListCreateAPIView):
permission_classes = [
permissions.IsAuthenticated
]
serializer_class = ProfileSerializer
name = 'profile-list'
##queryset = Profile.objects.all()
def get_queryset(self):
return self.request.user.profile.all()
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
But when I send the GET request I get this error:
{code}
'Profile' object has no attribute 'all'
{code}
So my question here is more general. Above view will work when I will use in my Profile model ForeignKey field instead of OneToOneField. In such case when in my view I will request for all objects which belongs to my user like so:
def get_queryset(self):
return self.request.user.profile.all()
I will get necessary data. But how to ask for objects when we have OneToOneField? It seems that the procedure is different, but I can not find it anywhere.
all() implies that there are many related objects, so this is applicable for reverse one-to-many (ForeignKey) and many-to-many (ManyToManyField) calls.
In case of one-to-one (OneToOneField), as the name suggests, one object can be related to only one of the object of the related model, hence there's no point of all-like methods here.
Now, the get_queryset method is supposed to return a queryset of objects, not a single one. So you can not use self.request.user.profile as that refers to only one instance.
You need to return a queryset with the only instance of Profile that is related to the requesting User:
def get_queryset(self):
return Profile.objects.filter(owner=self.request.user)
The documentation https://docs.djangoproject.com/en/2.2/topics/db/examples/one_to_one/ says that you would access the user profile by using user.profile. This makes sense, since it’s a OneToOneField and so we shouldn’t expect a QuerySet, just a single object.
I have created a pk field(not automatically created by the model).
when I want to use this pk name(in my case school_id) I got this error:
Generic detail view SchoolDetailView must be called with either an object pk or a slug.
although I am using the correct pk name(school_pk) I have used in my model in the url.
my code is as follows:
models.py:
from django.db import models
from django.urls import reverse
class School(models.Model):
school_pk = models.AutoField(primary_key=True)
name = models.CharField(max_length=256)
principal = models.CharField(max_length=256)
location = models.CharField(max_length=256)
def __str__(self):
return str(self.name)
def get_absolute_url(self):
return reverse("basic_app:school_detail",kwargs={'school_pk':self.school_pk})
views.py:
class SchoolListView(ListView):
model = models.School
template_name = 'basic_app/school_list.html'
class SchoolDetailView(DetailView):
context_object_name = 'school'
model = models.School
template_name = 'basic_app/school_detail.html'
urls.py:
urlpatterns = [
path('',views.SchoolListView.as_view(),name='school_list'),
path('<int:school_pk>/',views.SchoolDetailView.as_view(),name='school_detail'),]
when I try to go to school_detail page by going to http://127.0.0.1:8000/basic_app/1/ for example
I have this error:
Request Method: GET
Request URL: http://127.0.0.1:8000/basic_app/1/
Django Version: 2.0
Exception Type: AttributeError
Exception Value:
Generic detail view SchoolDetailView must be called with either an object pk or a slug.
I have tried to use get_object and/or get_queryset but I believe I am doing it the wrong way. if any one can help that will be great and highly appreciated. Thanks.
note:I dont want to omit school_pk field from my model and use PK that is automatically genertaed).
Add pk_url_kwarg = 'school_pk' to view SchoolDetailView. By default it is set to pk.
class SchoolDetailView(DetailView):
context_object_name = 'school'
model = models.School
template_name = 'basic_app/school_detail.html'
pk_url_kwarg = 'school_pk'
from rest_framework import serializers
class UserProfileSerializer(serializers.ModelSerializer):
"""Serializes data for User."""
url = serializers.URLField(source='profile_url')
def profile_url(self):
"""Return user profile endpoint."""
return reverse('user:profile_view', kwargs=self.context.get('id', None))
class Meta:
model = User
fields = ('id', 'url')
How can I access User.id within profile_url? There doesn't appear to be any id parameter within the context or at least I don't think I'm accessing properly.
Actually, you may want to change the field on your serializer to be:
url = serializers.SerializerMethodField('profile_url')
This will give the method profile_url an extra parameter, the object that is being serialized. So in your case, this becomes:
def profile_url(self, obj):
"""Return user profile endpoint."""
return reverse('user:profile_view', args=[obj.id,])
models.py
class Tag(models.Model):
name = models.CharField(max_length=64, unique=True)
slug = models.SlugField(max_length=255, unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Tag, self).save(*args, **kwargs)
urls.py
url(r'^tag/(?P<slug>[A-Za-z0-9_\-]+)/$', TagDetailView.as_view(), name='tag_detail'),
views.py
class TagDetailView(DetailView):
template_name = 'tag_detail_page.html'
context_object_name = 'tag'
Well, I thought this would work without any problem, because Django's generic DetailView will look for "slug" or "pk" to fetch its object. However, navigating to "localhost/tag/RandomTag" gives me an error:
error:
ImproperlyConfigured at /tag/RandomTag/
TagDetailView is missing a queryset. Define TagDetailView.model, TagDetailView.queryset, or override TagDetailView.get_queryset().
Does anyone know why this is happening...???
Thanks!!!
because Django's generic DetailView will look for "slug" or "pk" to fetch its object
It will, but you haven't told it what model it is to use. The error is very clear about this:
Define TagDetailView.model, TagDetailView.queryset, or override TagDetailView.get_queryset().
You can use the model or queryset attributes to do this, or the get_queryset() method:
class TagDetailView(...):
# The model that this view will display data for.Specifying model = Foo
# is effectively the same as specifying queryset = Foo.objects.all().
model = Tag
# A QuerySet that represents the objects. If provided,
# the value of queryset supersedes the value provided for model.
queryset = Tag.objects.all()
# Returns the queryset that will be used to retrieve the object that this
# view will display. By default, get_queryset() returns the value of the
# queryset attribute if it is set, otherwise it constructs a QuerySet by
# calling the all() method on the model attribute’s default manager.
def get_queryset():
....
There are a few different ways of telling the view where you want it to grab your object from, so have a read of the docs for more
I have two models - Contract and Supplier. Each supplier supplies a type of commodity. These are defined as follows:
class CommodityType(models.Model):
name = models.CharField(max_length=64)
def __unicode__(self):
return self.name
class Supplier(models.Model):
name = models.CharField(max_length=64)
type = models.ForeignKey(CommodityType)
def __unicode__(self):
return self.name
class Meta:
ordering = ['type', 'name']
class Contract(models.Model):
supplier = models.ForeignKey(Supplier)
clientNumber = models.CharField(max_length=32)
def __unicode__(self):
return u'%s, %s' % (self.supplier, self.clientNumber)
I want to have a listing of the Contracts in the Django Admin site. For each of the Contracts, I want to have the type from the referenced Supplier displayed. So, for example, if the associated supplier supplies Electricity, then I want to have that displayed in the listing of Contracts.
However, I cannot seem to find how this is done. I found this answer, but trying that gives me an ImproperlyConfigured error.
How can this be done?
What you probably need is the list_display
class ContractAdmin(admin.ModelAdmin):
list_display('clientNumber', 'supplier')
admin.register(Contract, ContractAdmin)
To allow __ in Admin for foreign key, You can use this snippet
From the snippet:
from django.contrib import admin
from django.db import models
def getter_for_related_field(name, admin_order_field=None, short_description=None):
"""
Create a function that can be attached to a ModelAdmin to use as a list_display field, e.g:
client__name = getter_for_related_field('client__name', short_description='Client')
"""
related_names = name.split('__')
def getter(self, obj):
for related_name in related_names:
obj = getattr(obj, related_name)
return obj
getter.admin_order_field = admin_order_field or name
getter.short_description = short_description or related_names[-1].title().replace('_',' ')
return getter
class RelatedFieldAdminMetaclass(admin.ModelAdmin.__metaclass__):
"""
Metaclass used by RelatedFieldAdmin to handle fetching of related field values.
We have to do this as a metaclass because Django checks that list_display fields are supported by the class.
"""
def __getattr__(self, name):
if '__' in name:
getter = getter_for_related_field(name)
setattr(self, name, getter) # cache so we don't have to do this again
return getter
raise AttributeError # let missing attribute be handled normally
class RelatedFieldAdmin(admin.ModelAdmin):
"""
Version of ModelAdmin that can use related fields in list_display, e.g.:
list_display = ('address__city', 'address__country__country_code')
"""
__metaclass__ = RelatedFieldAdminMetaclass
def queryset(self, request):
qs = super(RelatedFieldAdmin, self).queryset(request)
# include all related fields in queryset
select_related = [field.rsplit('__',1)[0] for field in self.list_display if '__' in field]
# Include all foreign key fields in queryset.
# This is based on ChangeList.get_query_set().
# We have to duplicate it here because select_related() only works once.
# Can't just use list_select_related because we might have multiple__depth__fields it won't follow.
model = qs.model
for field_name in self.list_display:
try:
field = model._meta.get_field(field_name)
except models.FieldDoesNotExist:
continue
if isinstance(field.rel, models.ManyToOneRel):
select_related.append(field_name)
return qs.select_related(*select_related)
#### USAGE ####
class FooAdmin(RelatedFieldAdmin):
# these fields will work automatically:
list_display = ('address__phone','address__country__country_code','address__foo')
# ... but you can also define them manually if you need to override short_description:
address__foo = getter_for_related_field('address__foo', short_description='Custom Name')
Recently, a library called django-related-admin released, which allows you to use foreign key attributes in Django admin change list list_display with '__' so easily, Specifically for this question, how to use this library in admin.py module is as follows:
admin.py
from related_admin import RelatedFieldAdmin
from related_admin import getter_for_related_field
class ContractAdmin(RelatedFieldAdmin):
# these fields will work automatically (and boolean fields will display an icon):
list_display = ('clientNumber','supplier__type__name')
# or you can also define them manually if you need to override short_description or boolean parameter:
supplierType = getter_for_related_field('supplier__type__name', short_description='supplier type', boolean=False)