I tried about 25 Stackoverflow links and nobody seems to have a working solution for this problem.
I created a custom queryset like this:
class ProjectQuerySet(models.QuerySet):
def get_active(self):
from apps.projectmanagement.models import Project
return self.filter(active=True)
class ProjectManager(models.Manager):
def get_queryset(self):
return ProjectQuerySet(self.model, using=self._db)
This works great if I start at the model like
Project.objects.get_active()
But if I want to use it in a relation, no luck so far:
employee.projects.get_active()
I always get this error:
AttributeError: 'ManyRelatedManager' object has no attribute 'get_active'
What I've tried so far:
I read that use_for_related_fields = True in the manager class is deprecated. Does not work anyway on django v2.1
Adding this in my model, as half the internet states:
class Project(models.Model):
...
objects = ProjectManager()
class Meta:
base_manager_name = 'objects'
Trying to avoid the RelatedManager and to work with a queryset:
employee.projects.all().get_active()
Any ideas what I've been doing wrong? And how would I solve this? Can't be too hard, right?
As the docs state, "Base managers aren’t used when querying on related models". The example they give is for going in the other direction, ie project.employee.
There is no way to do this using managers themselves. The best bet is to define a method on your model:
class Employee(models.Model):
...
def get_active_projects(self):
return self.projects.filter(active=True)
# or
return Project.objects.filter(employee=self).get_active()
Related
I came across this error in my django application after hitting submit on a create or edit form:
No URL to redirect to. Either provide a url or define a get_absolute_url method on the Model..
This was confusing because I have a get_success_url passed down through inheritance. To be clear, I have found the issue, but have no earthly idea why my solution worked.
Here was the code causing the error inside
.../views.py:
class FormViews():
model = Ticket
form_class = TicketForm
def get_success_url(self):
return reverse('tickets:index')
class TicketCreate(CreateView, FormViews):
template_name = 'tickets/ticket_create_form.html'
model = Ticket
form_class = TicketForm
class TicketUpdate(UpdateView, FormViews):
model = Ticket
form_class = TicketForm
template_name_suffix = '_update_form'
I created the FormViews class so there would not be any repeated code for the model, form_class, and get_success_url.
I was able to resolve this error by switching the parameters in my function definitions:
class TicketCreate(CreateView, FormViews) became class TicketCreate(FormViews, CreateView)
class TicketUpdate(UpdateView, FormViews) became class TicketUpdate(FormViews, UpdateView)
This fixed it. Now I redirect to the index page without any issues. Why is it that the get_success_url is recognized after switching the listed parent classes? I would have thought that the attributes and functions are inherited and recognized by Django regardless of order. Is this a Python or Django related issue?
In python every class has something called an MRO (Method Resolution Order), this explains it pretty well. Your FormViews (also for the most part classes in python are singular) is more of a mixin, I would call it as such: FormViewMixin.
Since CreateView and UpdateView are proper classes that have get_success_url defined, the order ABSOLUTELY matters. So I would put the things you want "discovered", first.
class TicketCreateView(FormViewMixin, CreateView):
...
is what you want.
I am trying to build a single course platorm where I will only hold lessons units materials where only people with membership will be able to see it , however when I try to do retrieve Lesson.course_allowed_mem_types.all() I got the following error 'ManyToManyDescriptor' object has no attribute 'all' , how can I fix this simple error?
class Lesson(models.Model):
content_title = models.CharField(max_length=120)
content_text = models.CharField(max_length=200)
thumbnail = models.ImageField(upload_to='static/xxx/xxx/xxx/xxx')
link = models.CharField(max_length=200, null=True)
allowed_memberships = models.ManyToManyField(Membership)
def __str__(self):
return self.content_title
views
def get_context_data(self, **kwargs):
context = super(bootCamp, self).get_context_data(**kwargs)
context['lessons'] = Lesson.objects.all()
user_membership = UserMembership.objects.filter(user=self.request.user).first()
user_membership_type = user_membership.membership.membership_type
course_allowed_mem_types = Lesson.allowed_memberships.all()
context['course_allowed_mem_types'] = course_allowed_mem_types
return context
You can query many-to-many related field only for model instance, not model class. It's not really clear what exactly is "all concrete allowed membership objects for a Lesson class" (Lesson.allowed_memberships.all()).
Is it "all membership objects related to any of existing lesson objects" or is it "all membership objects that can be related to a lesson object"?
Those are different queries, and Lesson.allowed_memberships.all() does not imply either, it's incorrect usage.
If you want the former, something like this could work
Membership.objects.filter(lesson__in=Lesson.objects.all())
(You already have this as context['lessons'] so use that instead, just showing the idea)
I think,
One lesson may have many memberships. so you are selecting all lessons with all memberships Lesson.allowed_memberships.all() .
Try selecting a single lesson then retrieve associated members
lesson = Lessons.objects.filter(pk=1)
course_allowed_mem_types = lesson.allowed_memberships.all()
If you want to create custom list like type, it is always a good idea to inherit from collections.abc.Iterable. It provides common operations required to work on such container types.
You can't just call .all() on any object/type, that type definition actually has to have all() method defined in class or parent class.
e.g.
class ListLike:
def __init__(self):
...
def all(self):
return some_iterator
Given the following models:
class Module(models.Model):
pass
class Content(models.Model):
module = models.ForeignKey(Module, related_name='contents')
class Blog(Module):
pass
class Post(Content):
pass
I would like to be able to get all the "post" objects owned by blog doing something like:
b = Blog.objects.get(pk=1)
b.posts.all()
However, I haven't figured out a good way of doing this. I can't use b.contents.all() as I need Post instances and not Content instances. I won't ever have a root content object, every content object is going to be subclassed, but I can't use abstract classes as I want a central table with all my content in it and then there will be content_blog etc tables for all the unique inherited pieces of content.
I also tried doing this
class Content(models.Model):
module = models.ForeignKey(Module, related_name='%(class)')
but that failed miserably as far as I could tell.
The simplest way might add a method to Blog model to return a Post queryset, like this:
class Blog(Module):
def _get_posts(self):
return Post.objects.filter(module=self)
posts = property(_get_posts)
The problem is you have to add method for every sub-model. The related_name seems only works for abstract base class.
This solution comes to my mind:
# ...
class Blog(Module):
#property
def posts(self):
return self.contents
class Post(Content):
pass
This way, doing blog.posts is the same as doing blog.contents:
>>> blog = Blog.objects.get(pk=1)
>>> blog.posts.all()
# [ ... ]
I'm trying to hold a kind of table of contents structure in my database. Simplified example:
models.py
class Section (models.Model):
title = models.CharField(max_length=80)
order = models.IntegerField()
class SectionClickable(Section):
link = models.CharField(max_length=80)
class SectionHeading(Section):
background_color = models.CharField(max_length=6)
views.py
sections = Section.objects.filter(title="Hello!")
for section in sections:
if(section.sectionheading):
logger.debug("It's a heading")
I need to do some processing operations if it's a SectionHeading instance, but (as in the Django manual), accessing section.sectionheading will throw a DoesNotExist error if the object is not of type SectionHeading.
I've been looking into alternatives to this kind of problem, and I'm skimming over Generic Foreign Keys in the contenttypes package. However, this seems like it would cause even more headaches at the Django Admin side of things. Could anyone advise on a better solution than the one above?
Edit: I avoided abstract inheritence because of the order field. I would have to join the two QuerySets together and sort them by order
well you could check the type:
if isinstance(section, SectionHeading)
but duck typing is generally preferred
edit:
actually, that probably won't work. the object will be a Section. but you can look for the attribute:
if hasattr(section, 'sectionheading')
or
try:
do_something_with(section.sectionheading)
except AttributeError:
pass # i guess it wasn't one of those
The solution I came up using involved an extra field pointing to the (rather useful) ContentType class:
class Section(models.Model):
name = models.CharField(max_length=50)
content_type = models.ForeignKey(ContentType,editable=False,null=True)
def __unicode__(self):
try:
return self.as_leaf_class().__unicode__()
except:
return self.name
def save(self, *args, **kwargs):
if(not self.content_type):
self.content_type = ContentType.objects.get_for_model(self.__class__)
super(Section, self).save(*args, **kwargs)
def as_leaf_class(self):
content_type = self.content_type
model = content_type.model_class()
if(model == Section):
return self
return model.objects.get(id=self.id)
If you're going through "base" object, I think this solution is pretty nice and comfortable to work with.
I've been using something similar to what second suggests in his edit:
class SomeBaseModel(models.Model):
reverse_name_cache = models.CharField(_('relation cache'), max_length=10,
null=True, editable=False)
def get_reverse_instance(self):
try:
return getattr(self, self.reverse_name_cache)
except AttributeError:
for name in ['sectionclickable', 'sectionheading']:
try:
i = getattr(self, name)
self.reverse_name_cache = name
return i
except ObjectDoesNotExist:
pass
Now, this isn't exactly pretty, but it returns the subclass instance from a central place so I don't need to wrap other statements with try. Perhaps the hardcoding of subclass reverse manager names could be avoided but this approach was enough for my needs.
OP here.
While second's answer is correct for the question, I wanted to add that I believe multi-table inheritence is an inefficient approach for this scenario. Accessing the attribute of the sub-class model would cause a query to occur - thus requiring a query for every row returned. Ouch. As far as I can tell, select_related doesn't work for multi-table inheritence yet.
I also ruled out ContentTypes because it wouldn't do it elegantly enough and seemed to require a lot of queries also.
I settled on using an abstract class:
class Section (models.Model):
title = models.CharField(max_length=80)
order = models.IntegerField()
class Meta:
abstract=True
ordering=['order']
Queried both tables:
section_clickables = SectionClickable.objects.filter(video=video)
section_headings= SectionHeading.objects.filter(video=video)
and joined the two querysets together
#Join querysets http://stackoverflow.com/questions/431628/how-to-combine-2-or-more-querysets-in-a-django-view
s = sorted(chain(section_headings, section_clickables), key=attrgetter('order'))
Lastly I made a template tag to check the instance:
from my.models import SectionHeading, SectionClickable
#register.filter()
def is_instance(obj, c):
try:
return isinstance(obj, eval(c))
except:
raise ObjectDoesNotExist('Class supplied to is_instance could not be found. Import it in the template tag file.')
so that in my template (HamlPy) I could do this:
- if s|is_instance:"SectionClickable"
%span {{s.title}}
- if s|is_instance:"SectionHeading"
%span{'style':'color: #{{s.color}};'}
{{s.title}}
The result is that I only used two queries, one to get the SectionClickable objects and one for the SectionHeading objects
I want a nice convenient attribute to do the following:
from django.contrib.auth.models import User
user = User.objects.get(id=2)
user.company
<Company: Big Company L.L.C>
I am currently solving this using lambda. In searching for an answer it looks like perhaps the "right" way to solve this would be to use types.MethodType but I can't seem to get my head around it. Yes, I have read Raymond excellent guide but I'm clearly missing something.. Here is my current solution for those who are interested..
# Defined Elsewhere
class User:
name = models.CharField(max_length=32)
class Company(models.Model):
users = models.ManyToManyField(User, related_name="companies", blank=True, null=True)
# Here is the meat of this..
class UserProfile(models.Model):
"""This defines Users"""
user = models.OneToOneField(User)
def get_company(self):
try:
companies = self.user.companies.all()[0]
except (AttributeError, IndexError):
return None
User.company = property(lambda u: UserProfile.objects.get_or_create(user=u)[0].get_company())
Right now this works.. But is there a better way - I'm not crazy about lambdas??
I'm not quite sure I understand correctly what your goal is, but from what I think I understand, it doesn't seem necessary to do any crazy stuff with descriptors here, let alone types.MethodType. A simple property is fine, and if you don't like the lambda, you can use an ordinary function decorated with #property:
class User:
name = models.CharField(max_length=32)
#property
def company(self):
return UserProfile.objects.get_or_create(user=self)[0].get_company())
Edit: If you can't touch the User class, you can create a derived class adding the desired property:
class MyUser(User):
#property
def company(self):
return UserProfile.objects.get_or_create(user=self)[0].get_company())
Building on #SvenMarnach's answer, you can still accomplish the same thing without using lambda. Though you still have to monkey-patch:
def _get_user_company(user):
return UserProfile.objects.get_or_create(user=user)[0].get_company()
User.company = property(_get_user_company)