Accessing the model attribute of UpdateView & friends

66 views
Skip to first unread message

Mikkel Kromann

unread,
Jun 5, 2018, 3:56:14 PM6/5/18
to Django users
Dear Django-users.

I'm slowly working towards a Django "data-warehouse" framework, where I can easily add a lot of models (each containing a table with data), while sticking to only few reusable views.

I've realised that in urls.py, it is possible to specify the model on which the UpdateView is going to do its magic.
This allows me to use a generic UpdateView (which I called ItemUpdateView) and template for all my models, which will save me a ton of almost identical lines of code.

However, in the generic ItemUpdateView, I of course need to specify the fields of the specific model chosen in urls.py.
As I ideally only want a few generic views (i.e. ItemCreateView, ItemUpdateView, ItemDeleteView and ItemListView), I've chosen to place the field array in my model definition, hoping to be able to access it from my generic Class Based Views

But how do I access the model attribute in (Item)UpdateView


thanks, Mikkel

From views.py
from django.views.generic import CreateView, ListView, UpdateView, DeleteView

class ItemUpdateView(UpdateView):
    template_name = "item_form.html"
# How do I access the model attribute of ItemUpdateView as given in urls.py?
# This line below returns the error "NameError: name 'model' not defined"
    fields
= model.fields

From urls.py
from django.urls import path
from . views import ItemUpdate
from . models import Region, Location

# Awesome! I can specify the model to be used by ItemUpdateView
urlpatterns
= [
    path
('update/region/<pk>',    ItemUpdateView.as_view(model=Region),     name='region_update'),
    path
('update/location/<pk>',  ItemUpdateView.as_view(model=Location),   name='location_update'),
]



From models.py (Region and Location are only two of my tables, I'd like to have say 20 or 30 models
from django.db import models

# Abstract class for our items including common methods, data and definitions
class ItemModel(models.Model):

    fields
= [ 'label', 'short', 'descr' ]
    label  
= models.CharField(max_length=10)
   
short   = models.CharField(max_length=15)
    descr  
= models.CharField(max_length=40)

   
def __str__(self):
       
return self.short

   
class Meta:
       
abstract = True

class Region(ItemModel):
    fields  
= [ 'label', 'short', 'descr' ]

class Location(ItemModel):
    fields  
= [ 'label', 'short', 'descr', 'region' ]
    region  
= models.ForeignKey(Region, on_delete=models.CASCADE)

class Plant(ItemModel):
    fields  
= [ 'label', 'short', 'descr', 'location', 'capex', 'opex', 'capacity' ]
    location
= models.ForeignKey(Location, on_delete=models.CASCADE)
    capex   = models.DecimalField(decimal_places=3, max_digits=8)
    opex    = models.DecimalField(decimal_places=3, max_digits=8)
    capacity= models.DecimalField(decimal_places=3, max_digits=8)


Andréas Kühne

unread,
Jun 5, 2018, 4:10:31 PM6/5/18
to django...@googlegroups.com
Hi,

Have you tried self.model? It should be present in all methods in the class?

Regards,

Andréas

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/ec4634e5-2279-49a7-9045-21712de87584%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Mikkel Kromann

unread,
Jun 6, 2018, 6:32:17 AM6/6/18
to Django users
Thanks for the advice, Andréas.
But the response is the same:

  File "C:\Users\ ... xxxxxxx   ... \items\views.py", line 7, in ItemUpdateView
    fields
= self.model.fields
NameError: name 'self' is not defined



As far as I understood, model is an attribute, not a method (though I'm new to Python and Django, so I'm unsure about this).
Does attribute/method make a difference in this case?

cheers, Mikkel

Regards,

Andréas

To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.

Daniel Germano Travieso

unread,
Jun 6, 2018, 9:01:05 AM6/6/18
to django...@googlegroups.com
Hello!

As you see on the documentation (https://docs.djangoproject.com/en/2.0/topics/class-based-views/) for the class based views, any arguments you pass to the class based view .as_view() method will override attributes of the class based view, so if you set MyCView.as_view(model=Foo) is the same as setting class MyCView: model=Foo directly.

Now to access the field names of the Model, you used to be able to call a .get_field_names() or something like that but that is depreciated since 1.10. Now to do that you should use the Model._meta to get_fields(). Check the documentation for the Model._meta to check its full potential.

Hope it helps!

Mikkel Kromann

unread,
Jun 6, 2018, 9:35:58 AM6/6/18
to Django users
Hello Daniel.

Thank you for your reply. Perhaps I was not explicit enough in describing my problem - it is far simpler than your solution indicates.
In my model definitions, I already have a list of the fields to be represented in the form of UpdateView - it is an attribute in the form of a simple array called "fields".
I don't want UpdateView to make a form for all columns of the model's data table, just the fields from my custom array in the Model objects.

My problem is simply: How to access my custom attribute "field" in the Model attribute passed to the Class Based View function.
As described, I get compilation errors, complaining that "model" and "self" are names not defined.
From the documentation (and my so far not too great Python understandign) it is not clear to me whether "Model" is simply the name of the model, or if it is the entire Model object instance.


thanks again, Mikkel

Daniel Germano Travieso

unread,
Jun 6, 2018, 10:12:06 AM6/6/18
to django...@googlegroups.com
Ok! I think I see your problem.

It is a deeper understanding of python classes that may be the problem.
You see, "model" and "self" are class atributes of the python class that houses your class based view (as every class in django is first a python class).

So, if you try to access the attribute "model" of the class, or the attribute "self", a attribute that houses the instance of the class, on it's class attributes definitions you will have problems, as the python interpreter only load these modules lazyly, not actually going through on finding the value for the attribute you referenced.

One solution is to have that value (for the attribute "field" as a @property that is a function that then the python interpreter will go through and execute the reference lookup.

Another solution is to set that value to be initially None, and set the value for the fields on the dispatch method of the view, something like 
class ItemUpdateView(UpdateView):
  template_name="foo.html"
  fields = None
 
  def dispatch (self, request, *args, **kwargs):
    self.fields = self.model.fields
    return super(ItemUpdateView, self).dispatch(request, *args, **kwargs)



But as of my understanding of your problem, you are trying to just customize which form fields to display to the user based on which model the user is updating, so you could just specify the form_class attribute on the path (just as you did with the model).

Hope it helps!


Mikkel Kromann

unread,
Jun 6, 2018, 1:51:10 PM6/6/18
to Django users
Thanks Daniel.

Both your proposed solutions worked very well.
For now, I don't have other information stuffed into my model definitions, so the urls.py approach could do.
However, if I decide to put more information into models.py, the def dispatch() approach could be useful.

One question though:
Do I understand correctly, that your version of def dispatch() overwrites the Django dispatch method from UpdateView.
As far as I can see, it checks the validity of the request method.
Could I use def __init__ instead of def dispatch


thanks + cheers, Mikkel


def dispatch(self, request, *args, **kwargs):
   
# Try to dispatch to the right method; if a method doesn't exist,
   
# defer to the error handler. Also defer to the error handler if the
   
# request method isn't on the approved list.
   
if request.method.lower() in self.http_method_names:
        handler
= getattr(self, request.method.lower(), self.http_method_not_allowed)
   
else:
        handler
= self.http_method_not_allowed
   
return handler(request, *args, **kwargs)


Daniel Germano Travieso

unread,
Jun 6, 2018, 6:24:03 PM6/6/18
to django...@googlegroups.com
I'm glad it helped.

As you can see, the dispatch method I wrote does override the default definition, but I return the super() dispatch method, so on the default flow the end result is the same (the validity of the request would be checked anyway, only after you set your class atribute). 
To avoid this work that would be invalid if there is an error on the request you could write a dispatch method that first gets the result of the super (). dispatch, check if it was ok then do the work you need and return the result of the dispatch after that.

And you could as well use the __init__ method for the class, but I'm not sure the .as_view() function works as a constructor like that, I would need to confirm the documentation, but there were some implementations that I worked with that customized the __init__ of the view class so it would probably work. A more philosophical question is if it is the proper location for that work to be done, as the __init__ method could be called even if the request is invalid, making your code try to do something weird.

Anyway, good luck on your project!


For more options, visit https://groups.google.com/d/optout.
--
[]'s
Daniel Germano Travieso
Engenharia de Computação UNICAMP

Mikkel Kromann

unread,
Jun 7, 2018, 4:51:44 AM6/7/18
to Django users
Thanks Daniel.

I actually tried to implement it in the __init__ but it didn't work.
I'll read through your explanation more carefully - I'm sure I could use a bit better understanding on the ways of Python and Django :)
Again, thank you for your very useful replies

cheers, Mikkel
Reply all
Reply to author
Forward
0 new messages