Manager isn't accessible via model instances - python

I'm trying to get model objects instance in another one and I raise this error :
Manager isn't accessible via topic instance
Here's my model :
class forum(models.Model):
# Some attributs
class topic(models.Model):
# Some attributs
class post(models.Model):
# Some attributs
def delete(self):
forum = self.topic.forum
super(post, self).delete()
forum.topic_count = topic.objects.filter(forum = forum).count()
Here's my view :
def test(request, post_id):
post = topic.objects.get(id = int(topic_id))
post.delete()
And I get :
post.delete()
forum.topic_count = topic.objects.filter(forum = forum).count()
Manager isn't accessible via topic instances

The error in question is caused when you try to access the Manager of a model through an instance of the model. You have used lower case class names. This makes it hard to say if the error is caused by an instance accessing the Manager or not. Since other scenarios that can cause this error are unknown I am proceeding on the assumption that you have somehow mixed up the topic variable so that you end up pointing to an instance of the topic model instead of the class.
This line is the culprit:
forum.topic_count = topic.objects.filter(forum = forum).count()
# ^^^^^
You have to use:
forum.topic_count = Topic.objects.filter(forum = forum).count()
# ^^^^^
# Model, not instance.
What is going wrong? objects is a Manager available at the class level, not to the instances. See the documentation for retrieving objects for details. Money quote:
Managers are accessible only via model classes, rather than from model instances, to enforce a separation between "table-level" operations and "record-level" operations.
(Emphasis added)
Update
See the comments from #Daniel below. It is a good idea (nay, you MUST :P) to use title case for class names. For instance Topic instead of topic. Your class names cause some confusion whether you are referring to an instance or a class. Since the Manager isn't accessible via <model> instances is very specific I am able to offer a solution.The error may not be so self evident always.

topic.__class__.objects.get(id=topic_id)

For django < 1.10
topic._default_manager.get(id=topic_id)
Though you should not use it like this. The _default_manager and _base_manager are private, so it's recomended to use them only if you're inside the Topic model, like when you want to use the Manager in a proprietary function let's say:
class Topic(Model):
.
.
.
def related(self)
"Returns the topics with similar starting names"
return self._default_manager.filter(name__startswith=self.name)
topic.related() #topic 'Milan wins' is related to:
# ['Milan wins','Milan wins championship', 'Milan wins by one goal', ...]

Could also be caused by a pair of parantheses too much, e.g.
ModelClass().objects.filter(...)
instead of the correct
ModelClass.objects.filter(...)
Happens to me sometimes when bpython (or an IDE) automatically adds parantheses.
The result, of course, is the same - you have an instance instead of a class.

if topic were a ContentType instance (which it is not), this would have worked:
topic.model_class().objects.filter(forum = forum)

I just had an issue similar to this error. And looking back at your code it seems that it could be your issue too. I think your issue is that your comparing "id" to "int(topic_id)" and topic_id is not set.
def test(request, post_id):
post = topic.objects.get(id = int(topic_id))
post.delete()
I'm guessing your code should use "post_id" not "topic_id"
def test(request, post_id):
post = topic.objects.get(id = int(post_id))
post.delete()

I got the same error below:
AttributeError: Manager isn't accessible via CustomUser instances
When I accessed Manager with request.user.objects as shown below:
"views.py"
from django.http import HttpResponse
def test(request):
print(request.user.objects)
return HttpResponse("Test")

Related

How to filter through Model of a many-to-many field?

I'm trying to implement a geofencing for a fleet of trucks. I have to associate a list of boundaries to a vehicle. On top of that one of the requirements is keep everything even once it is deleted for audit purposes. Therefore we have to implement soft delete on everything. This is where the problem lies. My many to many field does not conform to the soft delete manager, it includes both the active and the inactive records in the lookup dataset.
class Vehicle(SoftDeleteModel):
routes = models.ManyToManyField('RouteBoundary', through='VehicleBoundaryMap', verbose_name=_('routes'),
limit_choices_to={'active': True})
class VehicleBoundaryMap(SoftDeleteModel):
vehicle = models.ForeignKey(Vehicle, verbose_name="vehicle")
route_boundary = models.ForeignKey(RouteBoundary, verbose_name="route boundary")
# ... more stuff here
alive = SoftDeleteManager()
class SoftDeleteManager(models.Manager):
use_for_related_fields = True
def get_queryset(self):
return SoftDeleteQuerySet(self.model).filter(active=True)
As you see above I tried to make sure the default manager is a soft delete manager (ie. filter for active records only) and also try use limit limit_choices_to but that turn out to field the foreign model only not the "through" model I wanted. If you have any suggestions or recommendation I would love to hear from you.
Thanks!
First problem: your use of limit_choices_to won't work because as the documentation says:
limit_choices_to has no effect when used on a ManyToManyField with a custom intermediate table specified using the through parameter.
You are using through so limit_choices_to has no effect.
Second problem: your use of use_for_related_fields = True is also ineffective. The documentation says about this attribute:
If this attribute is set on the default manager for a model (only the default manager is considered in these situations), Django will use that class whenever it needs to automatically create a manager for the class.
Your custom manager is assigned to the alive attribute of VehicleBoundaryMap rather than objects so it is ignored.
The one way I see which may work would be:
Create a proxy model for VehicleBoundaryMap. Let's call it VehicleBoundaryMapProxy. Set it so that its default manager is SoftDeleteManager() Something like:
class VehicleBoundaryMapProxy(VehicleBoundaryMap):
class Meta:
proxy = True
objects = SoftDeleteManager()
Have through='VehicleBounddaryMapProxy' on your ManyToManyField:
class Vehicle(SoftDeleteModel):
routes = models.ManyToManyField('RouteBoundary',
through='VehicleBoundaryMapProxy',
verbose_name=_('routes'))
What about if you just do:
class Vehicle(SoftDeleteModel):
#you can even remove that field
#routes = models.ManyToManyField('RouteBoundary', through='VehicleBoundaryMap', verbose_name=_('routes'),
# limit_choices_to={'active': True})
#property
def routes(self):
return RouteBoundary.objects.filter(
vehicleboundarymap__active=True,
vehicleboundarymap__vehicle=self,
)
And now instead of vehicle.routes.clear() use vehicle.vehicleboundarymap_set.delete(). You will only lose the reverse relation (RouteBoundary.vehicles) but you can implement it back using the same fashion.
The rest of the M2M field features are disabled anyway because of the intermediate model.

post_generation with factory boy gives ManyRelatedManager when I wanted list

I'm clearly being a numpty, I will admit that at the very start. I'm using the following factory
class DataSheetFactory(factory.DjangoModelFactory):
class Meta:
model = models.myModel
django_get_or_create = ('key_field',)
key_field = factory.Sequence(lambda n: n)
... <additional fields>
#factory.post_generation
def product_contributors(self, create, extracted, **kwargs):
if not create:
return
if extracted:
for person in extracted:
self.product_contributors.add(person)
When I create this with PDS=DataSheetFactory(product_contributors= (<tuple of users>)) I expect a list of users in the field product_contributors, but I get a django.db.models.fields.related.ManyRelatedManager instead, so the tests won't work. What was I supposed to do, as I followed the "Common Recipes" here?
Just to clarify, I am asking how to use factory boy to create a list of users in a many-to-many field of a model. I tried what was suggested in the docs, and I get a ManyRelatedManager, not a list, so factory_boy generates unusable instances.
As pointed above, your issue comes from the magic I've put into factory_boy: the self in a #post_generation isn't the factory instance, but the actual object just generated.
Thus, the product_contributors is indeed an instance of Django's "RelatedManager", you can access the items with:
datasheet = DataSheetFactory()
contributors = datasheet.product_contributors.all()

How can I create an entity in the GAE datastore of a type determined at runtime?

I have a URL route that captures the name of the model for which the entity will be created.(e.g. '/New<model>/') Now, I have the model name as a string. How can I take that string and use it to create a new entity of this model to put in the datastore?
I'm building a custom admin interface for GAE with the ability to register new models and have forms built dynamically based on registered models. I need a way to then be able to take the posted data submitted by the admin user and put it into the datastore using the correct model which will be in the URL the form is posted to. I'm currently building a handler to process requests coming into that URL scheme but have hit this roadblock. Any help is greatly appreciated.
Although you could access the model class with
globals()[modelname]
if the model class is defined at the module level, as #TimHoffman points out in the comments, it is generally not a good idea to allow (potentially malicious) users arbitrary access to your globals.
Since you are generating model classes dynamically, a safer way would be to save those model classes in a dict:
model = { 'MyModel': MyModelClass, ... }
and then given the string name modelname, you could access the class with
model[modelname]
I didn't use globals to get at my class constructor. Instead, I made a variable that points to the current module and used getattr to capture my class constructor. Here's my handler in case anyone else wants to see what I did.
class NewThingHandler(BaseRequestHandler):
def get(self, thing):
self.redirect('/admin/')
def post(self, thing):
this_module = sys.modules[__name__]
ThisModel = getattr(this_module, thing)
arguments = {}
for property in ThisModel.properties().keys():
if type(ThisModel._properties[property]) is db.DateProperty:
this_date = map(int, self.request.get(property).split('/'))
this_date = datetime.date(this_date[2], this_date[0], this_date[1])
arguments[property] = this_date
continue
arguments[property] = self.request.get(property)
new_thing = ThisModel(**arguments)
new_thing.put()
self.redirect('/admin/')
thing is captured from the URL.

Python property() returning unlisted field?

The code I'm looking at is from django.contrib.auth.models.User
def _get_message_set(self):
import warnings
warnings.warn('The user messaging API is deprecated. Please update'
' your code to use the new messages framework.',
category=DeprecationWarning)
return self.**_message_set**
message_set = property(_get_message_set)
--where the devil is this _message_set field?
I suspect something of the automatic sort is happening here, but I'm not sure.
In Python, an attribute of an object does not need to be declared. In your case, it's set up in the constructor of the superclass models.Model.
Note that the internal attribute is prefixed with an underscore, whereas the external one (which happens to be a property) lacks one.
from django.contrib.auth.models:
class Message(models.Model):
"""
The message system is a lightweight way to queue messages for given
users. A message is associated with a User instance (so it is only
applicable for registered users). There's no concept of expiration or
timestamps. Messages are created by the Django admin after successful
actions. For example, "The poll Foo was created successfully." is a
message.
"""
user = models.ForeignKey(User, related_name='_message_set')
message = models.TextField(_('message'))
def __unicode__(self):
return self.message

Django model inheritance: create sub-instance of existing instance (downcast)?

I'm trying to integrate a 3rd party Django app that made the unfortunate decision to inherit from django.contrib.auth.models.User, which is a big no-no for pluggable apps. Quoting Malcolm Tredinnick:
More importantly, though, just as in Python you cannot "downcast" with
Django's model inheritance. That is, if you've already created the User
instance, you cannot, without poking about under the covers, make that
instance correspond to a subclass instance that you haven't created yet.
Well, I'm in the situation where I need to integrate this 3rd party app with my existing user instances. So, if hypothetically I am indeed willing to poke about under the covers, what are my options? I know that this doesn't work:
extended_user = ExtendedUser(user_ptr_id=auth_user.pk)
extended_user.save()
There's no exception, but it breaks all kinds of stuff, starting with overwriting all the columns from django.contrib.auth.models.User with empty strings...
This should work:
extended_user = ExtendedUser(user_ptr_id=auth_user.pk)
extended_user.__dict__.update(auth_user.__dict__)
extended_user.save()
Here you're basically just copying over the values from the auth_user version into the extended_user one, and re-saving it. Not very elegant, but it works.
I found this answer by asking on django-user mailing list:
https://groups.google.com/d/msg/django-users/02t83cuEbeg/JnPkriW-omQJ
This isn't part of the public API but you could rely on how Django loads fixture internally.
parent = Restaurant.objects.get(name__iexact="Bob's Place").parent
bar = Bar(parent=parent, happy_hour=True)
bar.save_base(raw=True)
Keep in mind that this could break with any new version of Django.
If you don't like __dict__.update solution you can do this:
for field in parent_obj._meta.fields
setattr(child_obj, field.attname, getattr(parent_obj, field.attname))
I am using Django 1.6, and my ExtendedUser model is from OSQA (forum.models.user.User). For some bizarre reason the above solutions with dict.__update__ and with setattr sometimes fail. This may have to do with some other models that I have, that are putting constrains on the user tables. Here are two more workarounds that you can try:
Workaround #1:
extended_user = ExtendedUser(user_ptr_id = user.pk)
extended_user.save() # save first time
extended_user.__dict__.update(user.__dict__)
extended_user.save() # save second time
Workaround #2:
extended_user = ExtendedUser(user_ptr_id = user.pk)
extended_user.__dict__.update(user.__dict__)
extended_user.id=None
extended_user.save()
That is, sometimes saving the new child instance fails if you set both pk and id, but you can set just pk, save it, and then everything seems to work fine.
There is an open bug for this very question:
https://code.djangoproject.com/ticket/7623
The proposed patch (https://github.com/django/django/compare/master...ar45:child_object_from_parent_model) is not using obj.__dict__
but creates an dictionary with all field values cycling over all fields.
Here a simplified function:
def create_child_from_parent_model(child_cls, parent_obj, init_values: dict):
attrs = {}
for field in parent_obj._meta._get_fields(reverse=False, include_parents=True):
if field.attname not in attrs:
attrs[field.attname] = getattr(parent_obj, field.attname)
attrs[child_cls._meta.parents[parent_obj.__class__].name] = parent_obj
attrs.update(init_values)
print(attrs)
return child_cls(**attrs)
create_child_from_parent_model(ExtendedUser, auth_user, {})
This method has the advantage that methods that are overwritten by the child are not replaced by the original parent methods.
For me using the original answers obj.__dict__.update() led to exceptions as I was using the FieldTracker from model_utils in the parent class.
What about something like this:
from django.forms.models import model_to_dict
auth_user_dict = model_to_dict(auth_user)
extended_user = ExtendedUser.objects.create(user_ptr=auth_user, **auth_user_dict)
#guetti's answer worked for me with little update => The key was parent_ptr
parent_object = parent_model.objects.get(pk=parent_id)
new_child_object_with_existing_parent = Child(parent_ptr=parent, child_filed1='Nothing')
new_child_object_with_existing_parent.save()
I wanted to create entry in my profile model for existing user, my model was like
from django.contrib.auth.models import User as user_model
class Profile(user_model):
bio = models.CharField(maxlength=1000)
another_filed = models.CharField(maxlength=1000, null=True, blank=True)
At some place I needed to create profile if not exists for existing user so I did it like following,
The example that worked for me
from meetings.user import Profile
from django.contrib.auth.models import User as user_model
user_object = user_model.objects.get(pk=3)
profile_object = Profile(user_ptr=user_object, bio='some')
profile_object.save()

Categories