I want to make gallery for users based on Photologue
app. In order to connect users' profiles to photologue's models I want to use OneToOne
. Also I want to ... lets say 'override' photologue's get_absolute_url
, which is used by templates.
# models
from photologue.models import Gallery
from profiles.models import UserProfile
class GalleryExtended(models.Model):
gallery = models.OneToOneField(Gallery)
user = models.ForeignKey(UserProfile, verbose_name=_('user'), on_delete=models.CASCADE)
def get_absolute_url(self):
return reverse('profiles_user:profiles_gallery-details', args=[self.user.user_url, self.gallery.slug])
# views
from photologue.views import Gallery
from profiles.models import UserProfile
from .models import GalleryExtended, PhotoExtended
def get_user_gallery_queryset(self):
user = get_object_or_404(UserProfile, user_url=self.kwargs['user_url'])
gallery = Gallery.objects.filter(galleryextended__user=user)
return gallery
class ProfileGalleryDateView(object):
date_field = 'date_added'
allow_empty = True
get_queryset = get_user_gallery_queryset
# site.com/username/gallery (shows photos + images with a filter by year)
class ProfileGalleryPhotoArchiveIndexView(ProfileGalleryDateView, ArchiveIndexView):
template_name = 'galleries/gallery_n_photo_archive.html'
So in view if I do
gallery = Gallery.objects.filter(galleryextended__user=user)
templates start to use Photologue's get_absolute_url
(I do not use the corephotologue
s url url(r'^photologue/', include('photologue.urls', namespace='photologue')),
as I integrate the app in my own url schema)
Is it possible to revert to something like this
gallery = GalleryExtended.objects.filter(user=user).***(get fields from Gallery)***
and avoid django.core.exceptions.FieldDoesNotExist: GalleryExtended has no field named 'date_added'
in order to start using get_absolute_url
from GalleryExtended
?
I know, it can easily be solved by extending photologue
s model via inheritance, but I want to know if is it possible to use OneToOne? because in some sources I have read it is recommended to use 1to1 instead of inheritance.
So, I think I understand now what you want to do. And it's wrong and hacky and bad in each and every way.
First of all - model inheritance is nothing to be afraid of, in fact it's exactly the opposite. The hustle you're experiencing now is a great example of what's in store if you decide not to follow one of OOP's most important principles. Arguments such as "I read something from somewhere" can't really convert me and I truly recommend that you use inheritance instead of 1to1 relations. Not to mention that using 1to1 relation instead of inheritance also creates an extra DB query every time you need to use the objects in the same place.
Despite not recommending to carry on with this setup, I can give you some possible versions how to achieve sth similar to what you want. It's not perfect because it's not meant to be used this way.
If we're on the same page now, you have a structure such as this:
class Model1(models.Model):
name = models.CharField(max_length=128)
state = models.CharField(max_length=128)
def some_method(self):
return self.name
class Model2(models.Model):
first = models.OneToOneField(Model1, related_name='second')
name = models.CharField(max_length=128)
def some_method(self):
return self.name + '2345678'
You want to query out all the Model2
s and call Model1
's some_method()
on them (correct if wrong).
This can be achieved with a hacky solution when you replace the self
argument with another model that fits the criteria.
I.e.
all_method_values = []
for obj in Model2.objects.all():
all_method_values.append(Model1.some_method(obj))
So how to write this in a more pythonic way and still keep the 1to1 relation? Change the method in question to a static method with an argument and if you need to call it as a bound method of either models, make them call the same static method with respective arguments. But code says more than 1000 words:
# In the model
def get_absolute_url(self):
return GalleryExtended.get_gallery_url(self.user.user_url, self.gallery.slug)
@staticmethod
def get_gallery_url(user_url, slug):
return reverse('profiles_user:profiles_gallery-details', args=[self.user.user_url, self.gallery.slug])
Now, instead of the ugliness shown above, you can call without ugly hacking:
all_method_values = []
for obj in Model2.objects.all():
all_method_values.append(Model1.get_gallery_url(obj.user.user_url, obj.gallery.slug))
For the record, I still recommend inheritance, but this works okay as well. Doing this small step removes the confusion of mixing two separate models' bound methods and clears up where the data is coming from.