Django: include get_absolute_url() in abstract model? - python

I'm working on a project that allows the user to fill in and save checklists needed in gastronomy.
As the checklists' content differ from one another, I decided to create an abstract base class 'checklists' with shared fields and have child models for differing attributes.
I want to find a way to dynamically create unique urls for each checklist through the abstract model.
The only way I can make it work so far is to include a get_absolute_url() in each child model - but this way I have to manually define a url in my urlpatterns and different views for each child model. Feels like unnecessary repetition.
I tried including the get_absolute_url() in my abstract model. However, this does not work because I can't seem to access the child's attributes in the abstract model.
Is there a way to include get_absolute_url() in my abstract base class?
I appreciate your help! Thanks a lot!
This is part of the code:
models.py
class Checklisten(models.Model):
# abstract model that contains shared features of all checklists
erlaubte_pruefer = models.ForeignKey(Pruefer, on_delete=models.CASCADE,
verbose_name='Prüfer')
pruefende_firma = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE, verbose_name='Unternehmen')
raum_verbindung = models.ManyToManyField(Raeume, verbose_name='Ort')
datum = models.DateField('Datum', auto_now_add=True, blank=False)
class Meta:
ordering = ['datum', 'pruefende_firma', 'raum_verbindung',
'erlaubte_pruefer']
abstract = True
verbose_name = 'Checkliste'
verbose_name_plural = 'Checklisten'
# can I include a get_absolute_url() with pk, datum AND a abbreviated 'bezeichnung' here?
class Checklisten_Schaedlinge(Checklisten):
insekten_befall = models.BooleanField('Insektenbefall', default=False)
befall_art = models.CharField('Insektenart des Befalls', blank=True,
null=True, max_length=40)
bezeichnung = 'Schädlingsmonitoring/Bekämpfung'
class Meta:
verbose_name = 'Schädlingsmonitoring/Bekämpfung'
verbose_name_plural = 'Schädlingsmonitoring/Bekämpfung'
def __str__(self):
return '%s - %s'%(self.datum, self.bezeichnung)
def get_absolute_url(self):
return reverse('checkliste-detail', kwargs={'pk' : self.pk,
'datum' : self.datum})
class Checklisten_Waagen_Thermometer(Checklisten):
WAAGE = 'WA'
THERMOMETER = 'TH'
ART = (
(WAAGE, 'Waage'),
(THERMOMETER, 'Thermometer')
)
werkzeug_art = models.CharField('Art', choices=ART, default=WAAGE,
max_length=40)
geeicht = models.BooleanField('geeicht', default=False)
naechster_eichtermin = models.DateField('Nächster Eichtermin')
bezeichnung = 'Erfassungsliste Waagen & Thermometer'
class Meta:
verbose_name = 'Erfassungsliste Waagen & Thermometer'
verbose_name_plural = 'Erfassungslisten Waagen & Thermometer'
def __str__(self):
return '%s - %s'%(self.datum, self.bezeichnung)
def get_absolute_url(self):
return reverse('checkliste-waage', kwargs={'pk' : self.pk,
'datum' : self.datum})
urls.py
urlpatterns = [
path('meine-checklisten/schaedlinge/<pk>/<datum>/, views.Checklisten_Schaedlinge_Detail_View.as_view(), name='checkliste-
schaedlinge'),
path('meine-checklisten/waagen/<pk>/<datum>/', views.Checklisten_Waagen_Detail_View.as_view(), name='checkliste-
waage'),]
views.py
class Checklisten_Schaedlinge_Detail_View(LoginRequiredMixin, DetailView):
model = Checklisten_Schaedlinge
template_name = 'checklisten/checkliste-schaedlinge-detail.html'
class Checklisten_Waagen_Detail_View(LoginRequiredMixin, DetailView):
model = Checklisten_Waagen_Thermometer
template_name = 'checklisten/checkliste-schaedlinge-detail.html'

Related

Filter field of manytomany field by a list

I would like to do the following with Django REST Framework:
Filter results based on a field of a manytomany field.
The query would look like this:
https://endpoint.com/api/artwork/?having_style=Modern,Contemporary
I would expect the result to contain all ArtWork objects which contain a relation to a Style object with name "Modern", "Contemporary" or both.
The code below is not working and I don't know why.
models.py
class Style(models.Model):
name = models.CharField(validators=[validate_style], max_length=100, unique=True)
class ArtWork(models.Model):
styles = models.ManyToManyField(Style, default=None)
filters.py
class ArtWorkFilter(filters_rest.FilterSet):
having_style = django_filters.Filter(field_name="styles__name", lookup_expr='in')
class Meta:
model = ArtWork
fields = ['having_style']
class StyleSerializer(serializers.ModelSerializer):
class Meta:
model = Style
fields = ('name',)
class ArtWorkSerializer(serializers.ModelSerializer):
styles = StyleSerializer(many=True)
class Meta:
model = ArtWork
fields = ('styles'/)
views.py
class ArtWorkViewSet(viewsets.ModelViewSet):
permission_classes = []
queryset = ArtWork.objects.all()
serializer_class = ArtWorkSerializer
filter_backends = [filters_rest.DjangoFilterBackend,]
filterset_class= ArtWorkFilter
pagination_class = CursorSetPagination
Thank you in advance!
Solution
I solved it by changing the ArtWorkFilter to
filters.py
class ArtWorkFilter(filters_rest.FilterSet):
having_style = django_filters.Filter(field_name="styles__name", lookup_expr='in')
class Meta:
model = ArtWork
fields = ['having_style']
def filter_by_style_name(self, queryset, name, value):
list_styles = value.split(',')
return queryset.filter(styles__name__in=list_styles)
Try adding method param in Filter declaration. Something like:
class ArtWorkFilter(filters_rest.FilterSet):
having_style = django_filters.Filter(field_name="styles__name", lookup_expr='in')
class Meta:
model = ArtWork
fields = ['having_style']
def filter_by_style_name(self, queryset, name, value):
list_styles = value.split(',')
return queryset.filter(styles__name__in=list_styles)
CartItem.objects.filter(cart=cart, product=product, attribute__in=attribute_list).annotate(num_attr=Count('attribute')).filter(num_attr=len(attribute_list))

Django Models Select a car model based on Car Make

Somewhat new to Django and I'm trying to create a car listing site. I've already ran into problems with the models. I can't seem figure out how I can create a model where if you select a particular make (e.g. Dodge) then you can select a model related to that make (e.g. Charger, Challenger, Viper etc.) or if you selected McLaren you could select from the 720s, 765lt, Senna, P1 etc.
models.py
class Make(models.Model):
make = models.CharField('Make', max_length=150)
class Meta:
ordering = ['make']
unique_together = ["make"]
verbose_name_plural = "Manufacturers"
def __str__(self):
return self.make
class CarModel(models.Model):
year = models.IntegerField(default=datetime.datetime.today().year)
make = models.ForeignKey(Make, on_delete=models.CASCADE)
model = models.CharField('Model', max_length=150)
trim = models.CharField('Trim', max_length=150, help_text='Trim level')
class Meta:
ordering = ['make', 'model', 'trim', 'year']
unique_together = ("year", "make", "model", "trim")
verbose_name_plural = "Models"
def __str__(self):
return f' {self.year} {self.make} {self.model} {self.trim}'
class CarListing(models.Model):
content = models.FileField("Media")
make = models.ForeignKey(Make, on_delete=models.CASCADE)
make_model = models.ForeignKey(CarModel, on_delete=models.CASCADE)
class Meta:
ordering = ['make_model']
verbose_name_plural = "Car Listings"
def __str__(self):
return f' {self.make_model.year} {self.make_model.make}
{self.make_model.model}
{self.make_model.trim} '
Use related_name for backwards compatibility.
class CarModel(models.Model):
year = models.IntegerField(default=datetime.datetime.today().year)
make = models.ForeignKey(Make, on_delete=models.CASCADE, related_name="models") # Note the related name here
model = models.CharField('Model', max_length=150)
trim = models.CharField('Trim', max_length=150, help_text='Trim level')
Then when you have a related name, you can easily access it by calling models on an instance
make = Make.objects.get(make="Dodge")
print(make.models) # Viper, Charger, Challenger, etc.
Note: make = Make.objects.get(make="Dodge") this will fire you an error if there are multiple records with the same query.
So you have to do something like this:
make = Make.objects.filter(make="Dodge") # return list of records`

Nested Relationship in Django Doesn't Work

These are my models here:
class Site(models.Model):
siteID = models.CharField(max_length=255, primary_key=True)
class EndDevice(models.Model):
class Meta:
unique_together = ("edevID", "siteID")
edevID = models.CharField(max_length=255)
siteID = models.ForeignKey(Site, related_name='endDeviceList', on_delete=models.CASCADE)
deviceCategory = models.BigIntegerField()
This is my serilaizer:
class DeviceSerializer(serializers.ModelSerializer):
class Meta:
model = EndDevice
fields = ("edevID", "siteID", "deviceCategory")
class SiteSerializer(serializers.ModelSerializer):
endDeviceList = DeviceSerializer(many = True, read_only=True)
class Meta:
model = Site
fields = ("siteID", "endDeviceList")
This is my view:
class IndividualSite(generics.RetrieveUpdateDestroyAPIView):
'''
PUT site/{siteID}/
GET site/{siteID}/
DELETE site/{siteID}/
'''
queryset = EndDevice.objects.all()
serializer_class = SiteSerializer
I am trying to get/put/delete results using this class and I am trying to get all the EndDevice instances which have same siteID. But my serialzer only shows the siteID and doesn't show the endDeviceList (which should have the instants of the model EndDevice)
The problem is quite similar to this link:django rest-farmework nested relationships.
I have been trying different ways to serialize the objects, I think this is probably the smartest way, but have been really unsucccessful. Any help will be appreciated.
The urls.py:
urlpatterns = [
urlpatterns = [path('site/<str:pk>/', IndividualSite.as_view(), name = "get-site"),]
And it is connected to the main urls.
you are using read_only field for the Foreign relationship, remove that, as read_only wont display them
class SiteSerializer(serializers.ModelSerializer):
endDeviceList = DeviceSerializer(many = True)

Django - Redirect to a subclass admin page

I'm creating a web application with Django.
In my models.py I have a class BaseProduct and a class DetailProduct, which extends BaseProduct.
In my admin.py I have BaseProductAdmin class and DetailProductAdmin class, which extends BaseProductAdmin.
I have another class called System, with a many to many relation with BaseProduct.
In the System admin page, I can visualize a list of the BaseProduct objects related to that system.
When I click on a product, the application redirect me to the BaseProduct admin page.
When a product of the list is a DetailProduct object, I would like to be redirected on the DetailProduct admin page instead.
Any idea on how to do this?
In models.py :
class BaseProduct(models.Model):
id = models.AutoField(primary_key=True, db_column='ID')
_prod_type_id = models.ForeignKey(
ProductTypes, verbose_name="product type", db_column='_prod_type_ID')
systems = models.ManyToManyField(
'database.System', through='database.SystemProduct')
def connected_to_system(self):
return self.systems.exists()
class Meta:
db_table = u'products'
verbose_name = "Product"
ordering = ['id', ]
class System(models.Model):
id = models.AutoField(primary_key=True, db_column='ID')
name = models.CharField(max_length=300)
def has_related_products(self):
""" Returns True if the system is connected with products. """
return self.products_set.exists()
class Meta:
managed = False
db_table = u'systems'
verbose_name = "System"
ordering = ['id', ]
class DetailProduct(BaseProduct):
options_id = models.AutoField(db_column='ID', primary_key=True)
product = models.OneToOneField(BaseProduct, db_column='_product_ID', parent_link=True)
min_height = models.FloatField(help_text="Minimum height in meters.")
max_height = models.FloatField(help_text="Maximum height in meters.")
def __init__(self, *args, **kwargs):
super(DetailProduct, self).__init__(*args, **kwargs)
if not self.pk:
self._prod_type_id = ProductTypes.objects.get(pk=9)
class Meta:
managed = False
db_table = 'detail_product'
verbose_name = "Detail product"
verbose_name_plural = "Detail products"
class SystemProduct(models.Model):
id = models.AutoField(primary_key=True, db_column='ID')
_system_id = models.ForeignKey(System, db_column='_system_ID')
_product_id = models.ForeignKey(BaseProduct, db_column='_Product_ID')
class Meta:
db_table = u'system_product'
unique_together = ('_system_id', '_product_id')
verbose_name = "system/product connection"
In my admin.py page:
class SystemProductInlineGeneric(admin.TabularInline):
model = SystemProduct
extra = 0
show_edit_link = True
show_url = True
class SystemProductForm(forms.ModelForm):
class Meta:
model = SystemProduct
fields = '__all__'
def __init__(self, *args, **kwargs):
""" Remove the blank option for the inlines. If the user wants to remove
the inline should use the proper delete button. In this way we can
safely check for orphan entries. """
super(SystemProductForm, self).__init__(*args, **kwargs)
modelchoicefields = [field for field_name, field in self.fields.iteritems() if
isinstance(field, forms.ModelChoiceField)]
for field in modelchoicefields:
field.empty_label = None
class SystemProductInlineForSystem(SystemProductInlineGeneric):
""" Custom inline, used under the System change page. Prevents all product-system
connections to be deleted from a product. """
form = SystemProductForm
raw_id_fields = ("_product_id",)
class SystemAdmin(admin.ModelAdmin):
inlines = [SystemProductInlineForSystem]
actions = None
list_display = ('id', 'name')
fieldsets = [('System information',
{'fields': (('id', 'name',), ),}),
]
list_display_links = ('id', 'configuration',)
readonly_fields = ('id',)
save_as = True
If I understand correctly, your question is how to change the InlineAdmin (SystemProductInlineForSystem) template so the "change link" redirects to the DetailProduct admin change form (instead of the BaseProduct admin change form) when the product is actually a DetailProduct.
I never had to deal with this use case so I can't provide a full-blown definitive answer, but basically you will have to override the inlineadmin template for SystemProductInlineForSystem and change the part of the code that generates this url.
I can't tell you exactly which change you will have to make (well, I probably could if I had a couple hours to spend on this but that's not the case so...), so you will have to analyze this part of the code and find out by yourself - unless of course someone more knowledgeable chimes in...

How do I set a field values of a django/mezzanine model based on the title of its foreignKey?

I would like to configure the mezzanine fork of django-filebrowser to create a subfolder when uploading an image, based on the title of a particular post within my mezzanine app.
The file field of that model requires setting "upload_to=", but I don't understand how I can make it point to a field value of its parent/foreignKey instance, rather than just a static value. I have tried defining a callable which points to exhibPost.title, as well as using it directly in the field as shown below.
I'd love to hear an explanation, I'm sure I'm misunderstanding something quite major about django here... Thanks
models.py - (imports omitted)
class exhibPost(Displayable, RichText,):
"""
An exhib post.
"""
def __unicode__(self):
return u'%s' % (self.id)
showstart = models.DateField("Show Starts")
showend = models.DateField("Show Ends")
start_time = models.TimeField(null=True, blank=True)
end_time = models.TimeField(null=True, blank=True)
summary = models.CharField(max_length=200,null=True,default=get_val)
class Meta:
verbose_name = _("exhib post")
verbose_name_plural = _("exhib posts")
ordering = ("-publish_date",)
class exhibImage(Orderable):
'''
An image for an exhib
'''
exhibPostKey = models.ForeignKey(exhibPost, related_name="images")
file = FileField(_("File"), max_length=200, format="Image",
upload_to=upload_to(
"theme.exhibImage.file",
----> exhibPost.title
)
)
class Meta:
verbose_name = _("Image")
verbose_name_plural = _("Images")
EDIT
#Anzel
The function I'm referring to is defined in my models as
def get_upload_path(instance, filename):
return os.path.join(
"user_%d" % instance.owner.id, "car_%s" % instance.slug, filename)
...and I call it in the same place that I arrowed originally.

Categories