Attaching extra information to model instance - django - python

I have a django model that I want to attach an extra piece of information to, depending on the environment the instance is in (which user is logged in). For this reason, I don't want to do it at the database level.
Is this okay to do? Or are there problems that I don't foresee?
in models.py
class FooOrBar(models.Model):
"""Type is 'foo' or 'bar'
"""
def __init__(self, type):
self.type = type
in views.py
class FooCheck(FooOrBar):
"""Never saved to the database
"""
def __init__(self, foo_or_bar):
self.__dict__ = foo_or_bar.__dict__.copy()
def check_type(self, external_type):
if external_type == 'foo':
self.is_foo = True
else:
self.is_foo = False
foos_or_bars = FooOrBar.objects.all()
foochecks = map(FooCheck, foos_or_bars)
for foocheck in foochecks:
foocheck.check_type('foo')
extra credit question: Is there a more efficient way of calling a method on multiple objects i.e. replacing the last forloop with something clever?

Okay, this does not work. Trying to delete a FooOrBar objects throws a complaint about
OperationalError at /
no such table: test_FooCheck
To get around this I'm just not going to inherit from FooOrBar, but if anyone has a suggestion on a better way to do it I'd be interested in hearing it

I had a similar issue, I did something like:
class Foo(models.Model):
# specific info goes here
class Bar(models.Model):
# specific info goes here
class FooBar(models.Model):
CLASS_TYPES = {
"foo":Foo,
"bar":Bar
}
type = models.CharField(choices=CLASS_TYPES)
id = models.IntegerField()
#field to identify FooBar
then you can get the object back using
object = FooBar.CLASS_TYPES[instance.type].objects.get(id=instance.id)
where instance is the FooBar instance

Related

Message Object has no attribute 'fields' when updating model field across apps

I have two apps menu and table. In app table, I have this model:
class Table(models.Model):
available = models.BooleanField(verbose_name="Availability", default=True)
def set_availability(self, avail=False):
self.fields['available'] = avail
self.save()
def __str__(self):
return "Table " + str(self.id_num)
In one of the views of app menu, I have the following call:
from table.models import Table
def menu_category_view(request, table_pk):
table = Table.objects.get(pk=table_pk)
if table.available:
table.set_availability(False)
...
return render(request,
...)
When my template calls this view, I receive this error message 'Table' object has no attribute 'fields'. Here, I am trying to update the value of field available of the instance being called (from True to False). And I got this implementation suggested from a book. Is this the right way to update model instance field value? Thanks.
Just set the attribute.
def set_availability(self, avail=False):
self.available = avail
self.save()
Though, it's questionable whether or not set_<field> methods like this are particularly useful. You could work with the object almost as easily:
if table.available:
table.available = False
table.save()

how can I fix ManyToManyDescriptor from a lesson class model - view?

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

How do I override related_name in inherited/child objects?

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()
# [ ... ]

Equivalent of objects.latest() in App Engine

What would be the best way to get the latest inserted object using AppEngine ?
I know in Django this can be done using
MyObject.objects.latest()
in AppEngine I'd like to be able to do this
class MyObject(db.Model):
time = db.DateTimeProperty(auto_now_add=True)
# Return latest entry from MyObject.
MyObject.all().latest()
Any idea ?
Your best bet will be to implement a latest() classmethod directly on MyObject and call it like
latest = MyObject.latest()
Anything else would require monkeypatching the built-in Query class.
Update
I thought I'd see how ugly it would be to implement this functionality. Here's a mixin class you can use if you really want to be able to call MyObject.all().latest():
class LatestMixin(object):
"""A mixin for db.Model objects that will add a `latest` method to the
`Query` object returned by cls.all(). Requires that the ORDER_FIELD
contain the name of the field by which to order the query to determine the
latest object."""
# What field do we order by?
ORDER_FIELD = None
#classmethod
def all(cls):
# Get the real query
q = super(LatestMixin, cls).all()
# Define our custom latest method
def latest():
if cls.ORDER_FIELD is None:
raise ValueError('ORDER_FIELD must be defined')
return q.order('-' + cls.ORDER_FIELD).get()
# Attach it to the query
q.latest = latest
return q
# How to use it
class Foo(LatestMixin, db.Model):
ORDER_FIELD = 'timestamp'
timestamp = db.DateTimeProperty(auto_now_add=True)
latest = Foo.all().latest()
MyObject.all() returns an instance of the Query class
Order the results by time:
MyObject.all().order('-time')
So, assuming there is at least one entry, you can get the most recent MyObject directly by:
MyObject.all().order('-time')[0]
or
MyObject.all().order('-time').fetch(limit=1)[0]

Django, Python, trying to change field values / attributes in object retrieved from DB objects.all call, not working

I'm trying to change a specific field from a field in an object that I retrieved from a django db call.
class Dbobject ()
def __init__(self):
dbobject = Modelname.objects.all()
def test (self):
self.dbobject[0].fieldname = 'some new value'
then I am able to access a specific attribute like so:
objclass = Dbobject()
fieldvalue = dbobject.dbobject[0].fieldname
but I want to be able to use the "test" method of the Dbobject class to try to change the specific value on an object's attribute value, but it isn't changing it. I am stumped by this as this is how I thought I am supposed to change an object's attribute value.
I'm not sure if this is the problem or not, but I think you might be missing a save() method.
from models import Person
p = Person.objects.get(pk=100)
p.name = 'Rico'
p.save() # <== This writes it to the db. Is this what you're missing?
Above is the simple case. Adapted for what you wrote above, it'd be like:
dbobject.dbobject[0].fieldname = 'some new value'
dbobject.dbobject[0].save()
or, I'd write it more like:
rec = dbobject.dbobject[0]
rec.fieldname = 'some new value'
rec.save()
Also note that depending on whether and how you are using transactions, you may or may not see a change to the database until you commit.
I am not totally sure what you are trying to achieve, but shouldn't it be something like:
class Dbobject ():
def __init__(self):
self.dbobject = Modelname.objects.all()
def test (self):
self.dbobject[0].fieldname = 'some new value'

Categories