Access Django SubClass Method from SuperClass - python

Okay, so I have two Django models:
class Ticket(BaseModel):
name = models.CharField(max_length=200)
description = models.TextField(blank=True, null=True)
def get_absolute_url(self):
return '/core/group/ticket/{0}/'.format(self.id)
class ProjectTicket(Ticket):
project = models.ForeignKey('Project', on_delete=models.DO_NOTHING)
phase = models.ForeignKey('ProjectPhase', blank=True, null=True, on_delete=models.DO_NOTHING)
def get_absolute_url(self):
return '/different/url/structure'
Now, I'm querying all Ticket objects with Ticket.objects.all(). This will return all Ticket objects, including some that are ProjectTicket subclasses.
What I'd like to be able to do is access the subclass get_absolute_url() when the objects in question are actual subclassed ProjecTicket objects.
I know that I can get the parent class from the subclass, but I want to be able to do the reverse.
Has anyone achieved something like this? If so, what approach did you take?

Here's one way that I can think of right now:
I'm sure you know that inheriting django models creates a OneToOne relationship with the parent. So, the Ticket objects which are also instances of ProjectTicket class, will have an attribute called projectticket on them. You can check for this value and return the url accordingly:
class Ticket(...):
...
def get_absolute_url(self):
if hasattr(self, 'projectticket'):
return self.projectticket.get_absolute_url()
else:
return '/core/group/ticket/{0}/'.format(self.id)

Related

Django rest Framework: How to create a custom, auto-generating string field?

I'm using the generic Django Rest Framework classes for an application, but I'd like to be able to auto-generate a short customer order ID whenever a new order instance is created.
The generic classes are awesome, but small tweaks can get messy fast. I'm wondering if there's a way to do the auto-generation in models.py, in the spirit of classes like AutoIncrement or the way the primary key integer is created (but not as a primary key).
Let's say I have a model
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
details = models.CharField(null=True, max_length=200)
#property
def title(self):
return f"{self.user.first_name}'s, order"
and I want to add a new field, order_reference. And lets say I want to add some custom logic, like
#before_createish_thing
def order_reference(self):
# Return something like XYZ2021-000-23
return f"XYZ-{datetime.now().year}-000-{self.order_id}"
somewhere.
I don't suppose there's anything like a before_create decorator along the same lines as the #property decorator?
My view is as simple as
class OrderList(generics.ListCreateAPIView):
queryset = Order.objects.all()
serializer_class = OrderSerializer
hence my reluctance to hack it apart just to get to the create() method.
Is there any way to do this, ideally by somehow placing the logic in the model file?
You could simply do something like this:
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
details = models.CharField(null=True, max_length=200)
creation_date = models.DateField(auto_now_add=True)
#property
def title(self):
return f"{self.user.first_name}'s, order"
#property
def order_reference(self):
return f"XYZ-{self.creation_date.year}-000-{self.order_id}"
Also storing the date can allow you to change later your order_reference property and have access to the day, month, year for example. Or even use a DatetimeField. It always good to have the creation date, for future requirements.

Correctly using subclasses/proxies in django that have self referential foreign keys?

I have two classes, with a super class. In essence the two classes are concrete classes on a tree. One is a leaf, one is a branch. They share properties defined in the super class.
None of the below classes are finished. I've tried both making the superclass abstract, and the subclasses proxies. Hopefully the code below explains what I'm trying to achieve.
This is the 'super class'
class Owner(models.Model):
name = models.CharField(max_length=200)
def __unicode__(self):
return self.name
class Meta:
abstract=True
This is the 'leaf'
class User(Owner):
pass
This is the 'branch'.
class Group(Owner):
head = models.ForeignKey(User)
members = models.ManyToManyField(Owner,through='Membership')
This shows how a user can belong to a group by a membership.
class Membership(models.Model):
date_joined = models.DateField()
user = models.ForeignKey(Owner)
group = models.ForeignKey(Group)
My restrictions are that each user can belong to many groups (via the linker Membership). Each group can be a member of a single group.
This fails because I'm referencing Owner in both the membership as the user, and in the group members. I feel like this is the sort of thing I could solve with generics in Java, but thats not going to help me here.
I've also seen ContentTypes used for this sort of thing, but they seem too complicated for what I'm trying to do. Am I wrong? I can't figure out how to apply that paradigm to my example. I also found this question but I'm still not certain on how it should be implemented.
You can't have foreign key fields pointing to an abstract class (there is no table in the DB for an abstract class).
You'll probably need to implement self-referential foreign key for each Group to belong to zero or one group. Something like this:
class Base(models.Model):
name = models.CharField(max_length=200)
def __unicode__(self):
return self.name
class Meta:
abstract=True
class User(Base):
groups = models.ManyToManyField('Group', through='Membership', related_name='members')
class Group(Base):
head = models.ForeignKey(User)
parent = models.ForeignKey('self', blank=True, null=True, related_name='children')
def descendants(self, **kwargs):
qs = self.children_set.filter(**kwargs)
for group in self.children_set.all():
qs = qs | group.descendants(**kwargs)
return qs
class Membership(models.Model):
date_joined = models.DateField()
user = models.ForeignKey(User)
group = models.ForeignKey(Group)
The Base class above does nothing other than dictate that each inherited class has a name field in their respective DB table and __unicode__ method- nothing more. You could simply copy and paste the name field and __unicode__ method into User and Group and it would be functionally identical. Having a common abstract parent doesn't create any DB relationships and you can't use it to query the DB.
I can't really see any reason for using proxy classes in this situation.

Create Django Objects outside of Admin

I'm new to django and while the admin site is useful I need to be able to do simple functions without it. I would like to automatically manage my objects.
Is it possible to create objects outside of the admin site?
I know one way to do it
Class Foo (models.Model):
name = models.CharField(max_length=100)
def __unicode__(self):
return self.name
def create(cls, name, url):
bar = cls(name=name)
return bar
s1 = Foo.create("THIS IS A NAME")
s1.save()
But I'm having trouble adding new variable to the object
How do I add unique variables to each object
thanks
Sorry I am not sure about your question. If you want to have a unique field for your model you can do
yourUniqueFieldName = models.TextField(max_length=100, unique=True)
And this for a pair of (or more) unique fields you can do
class Meta:
unique_together = ('field1', 'field2',)
Official documents is always your best friend as a start:
https://docs.djangoproject.com/en/dev/ref/models/fields/

Tastypie resource not showing newly created objects (date filtering issue)

I have a model with a custom manager with the purpose of filtering "active" objects, i.e. objects which have a start_date lower than the current time and an end_date greater than the current time.
This is the relevant part of my models.py:
from django.utils.timezone import now
class ActiveObjectManager(models.Manager):
def get_query_set(self):
return super(ActiveObjectManager, self).get_query_set().\
filter(start_date__lt=now(), end_date__gt=now())
class Object(models.Model):
start_date = models.DateTimeField(_('Service start date'), \
auto_now_add=False, null=False, blank=False)
end_date = models.DateTimeField(_('Service end date'), auto_now_add=False, \
null=False, blank=False)
...
objects = models.Manager()
objects_active = ActiveObjectManager()
This manager works great across the application and in a Django shell. However, if I create an object in the admin interface, and set the start_date to the "now" selector, the API provided by tastypie isn't showing this newly created object (though it does show older objects). The admin list correctly shows the new object as active.
This is the relevant part of my api.py:
from app.models import Object
class ActiveObjectResource(ModelResource):
modified = fields.BooleanField(readonly=True)
class Meta:
resource_name = 'activeobjects'
queryset = Object.objects_active.all()
My strong suspicion is that, as the class ActiveObjectResource is being interpreted once, the couple of now() calls are only being executed once, i.e., the API subsystem is always calling filter() with the same values for the start_date__lt and end_date__gt parameters (the value returned by now() immediately after I run manage.py runserver).
This problem persists even when I do the filtering right in the resource class like this:
class ActiveObjectResource(ModelResource):
...
class Meta:
queryset = Object.objects.\
filter(start_date__lt=now(), end_date__gt=now())
Also, the problem persists if I pass callables like this:
class ActiveObjectResource(ModelResource):
...
class Meta:
queryset = Object.objects.filter(start_date__lt=now, end_date__gt=now)
Is there a way I can rewrite ActiveObjectManager or ActiveObjectResource to overcome this?
Update:
OK, it seems I need to override get_object_list to achieve per-request alterations to the queryset, like:
class ActiveObjectResource(ModelResource):
class Meta:
queryset = Object.objects.all()
def get_object_list(self, request):
return super(MyResource, self).get_object_list(request).\
filter(start_date__lt=now, end_date__gt=now)
But I hate to duplicate this logic when I already have a custom manager at the model level to do this work for me.
So my question is: how can I use my custom model manager from within my ModelResource?
Well, about queryset in ModelResource.Meta. Here's the excerpt from the tastypie documentation:
If you place any callables in this, they’ll only be evaluated once (when the Meta class is instantiated). This especially affects things that are date/time related. Please see the :ref:cookbook for a way around this.
Here it goes:
A common pattern is needing to limit a queryset by something that changes per-request, for instance the date/time. You can accomplish this by lightly modifying get_object_list
So, yeah, seems like the only way to achieve what you are trying to do is to declare get_object_list.
New Update: since get_object_list is just a return self._meta.queryset._clone(), try something like that:
class ActiveObjectResource(ModelResource):
class Meta:
queryset = Object.objects_active.all()
def get_object_list(self, request):
return Object.objects_active.all()

Update intermediate model object using generic view in django

How can i update the existing object of intermediate model using generic view?
class Person(models.Model):
name = models.CharField(max_length=128)
def __unicode__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __unicode__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
Currently i'm using generic views concept to update object, but I'm facing problem hoe to update field which exist in intermediate model?
If i generate modelform for Group class, then how can i update the associated field (intermediate model field) using generic view concept?
In above i want to update invite reason field
Thanks in advance
I think there are some missing views in generic or class-based views (which I highly recommend you if your are not already using them), and other people thinks in the same way...
Take a look at django-extra-views project, it implements those missing views.

Categories

Resources