Django Splitting models from models.py file - python

I am trying to separate all the models from models.py file.What I am doing is mentioned in this link. But Problem is My one model is django.contrib.auth.user and I am wring one function in models.py as follows to generate token.
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
So how do I import that thing in _init_.py file as we are importing model as
from myapp.models.foo import Foo

You should only have either models.py or models/__init__.py, and it seem like you have both. One of these modules will probably shadow the other, so don't have both (i.e. get rid of the models.py)

If I'm following your question correctly, you don't need to import User into __init__.py. You just need to import it in the file in which you declare create_user_profile. The import itself is standard:
from django.contrib.auth.models import User

You can't import a command, but for example importing the function defined above it, ensures running the connect call. Function calls in the body of the models.py file are also executed of the same raeson (i.e. models are imported).
# p.py:
print "hello" # a command executed while importing anything
def x(): # a definition that can be imported
pass
# python shell
>>> from p import x
hello
>>>

Related

in django 1.8, how to set sender for post_migrate and post_syncdb signal receiver when a custom user model is set?

Following is my code in the signals.py file placed in the package where the auth model is defined.
#receiver(post_migrate, sender=settings.AUTH_USER_MODEL)
def define_groups(sender, **kwargs):
# Create groups
Group.objects.get_or_create(name='Promoter')
Group.objects.get_or_create(name='Client')
Group.objects.get_or_create(name='Superuser')
Group.objects.get_or_create(name='Staff')
The documentation (https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#referencing-the-user-model) states that it should be set as
sender=settings.AUTH_USER_MODEL
while this only works for post_save as mentioned in the documentation example.
I've already tried get_user_model() and also directly using the my_custom_user.models.
get_user_model() returns an error, while setting models as sender works just fine, as -
from . import models
#receiver(post_syncdb, sender=models)
def define_groups(sender, **kwargs):
# Create groups
Group.objects.get_or_create(name='Promoter')
Group.objects.get_or_create(name='Client')
Group.objects.get_or_create(name='Superuser')
Group.objects.get_or_create(name='Staff')
But according to documentation this is not the right way to refer a custom user model and is just an ugly workaround.
Would someone please be able to help me with a solution so i can add these Groups with the first migration of user model.
Thank You
EDIT : using get_user_model() returns the following error -
django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.
The sender for the post_migrate method is never a model (custom or otherwise), it is the AppConfig instance for the app which was installed.
The docs give the following example for connecting your signal handler in the ready method.
from django.apps import AppConfig
from django.db.models.signals import post_migrate
def my_callback(sender, **kwargs):
# Your specific logic here
pass
class MyAppConfig(AppConfig):
...
def ready(self):
post_migrate.connect(my_callback, sender=self)
Similarly, the sender for post_sync_db signal (note the signal is deprecated) is the module containing the models which were installed.

How to Lazy Load a model in a managers to stop circular imports?

In Django you can create managers for your models. I do this by added a new file called managers.py and in my model objects = MyManager().
To stop circular imports I do self.model. However, if I need to reference a different model in my manager i.e.
from models import SecondModel
second= SecondModel(name=test).save()
self.model(second=second)
I get the following error: ImportError: cannot import name SecondModel
So is there a way in Django to lazy load a model?
The currently accepted answer is deprecated as of Django 1.7; from this answer, you can adapt your code like this.
from django.apps import apps
class SomeModelManager(...):
...
def some_function(self):
model = apps.get_model(app_label='your_app', model_name='YourModel')
You have a few options:
1. Import by name
Django has a utility function for importing by string name so you don't need to import yourself. There are several methods available for this (see this question: Django: Get model from string?)
from django.db.models.loading import get_model
class SomeModelManager(...):
...
def some_function(self):
model = get_model('your_app', 'YourModel')
object = model()
2. Imports at the bottom
Add the import at the bottom of the managers.py file and make sure to simply import the module and not the models themselves.
So...
models.py:
import managers
class SomeModel(models.Model):
...
objects = managers.SomeModelManager()
managers.py
class SomeModelManager(...):
...
def some_function(self):
object = models.SomeOtherModel()
import models

Patching(mocking) forms form in django tests

I tried to mock form with mock.patch and can`t. I have this code
forms.py
class CreatePostForm(object):
pass
views.py:
from forms import CreatePostForm
def doit():
print CreatePostForm()
and I want to to test this view in isolation. I tried to patch form with mock.patch and i wrote something like that:
tests.py:
from mock import patch
import views
with patch('forms.CreatePostForm') as Form:
views.doit()
I tried to google for solution and find nothing
Answered: thanks #dstanek for good answer and good sample of code
When you use patch you specify the target of the object you want to mock. This is usually the place where it is imported, not where it is defined.
This is because by the time your test runs the views module has already been imported. If you are importing the class like I'm doing in my example below then the views module will contain a reference to the forms.CreatePostForm. So changing forms.CreatePostForm would not change this reference. Things would be different if you imported the module as specified forms.CreatePostForm in your view.
I've included an extremely minimal example below.
forms.py
class CreatePostForm(object):
pass
views.py:
from forms import CreatePostForm
def doit():
print CreatePostForm()
tests.py:
from mock import patch
import views
with patch('views.CreatePostForm') as Form:
views.doit()

Django ORM - mock values().filter() chain

I am trying to mock a chained call on the Djangos model.Manager() class. For now I want to mock the values() and filter() method.
To test that I created a little test project:
Create a virtual environment
Run pip install django mock mock-django nose django-nose
Create a project django-admin.py startproject mocktest
Create an app manage.py startapp mockme
Add django_nose and mocktest.mockme to INSTALLED_APPS (settings.py)
Add TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' to settings.py
To verfiy that everything is setup correctly I ran manage.py test. One test is run, the standard test Django creates when you create an app.
Next thing I did was to create a very simple model.
mockme/models.py
from django.db import models
class MyModel(models.Model):
name = models.CharField(max_length=50)
Next thing I did was to create a simple function that uses MyModel. That's the function I want to test later.
mockme/functions.py
from models import MyModel
def chained_query():
return MyModel.objects.values('name').filter(name='Frank')
Nothing special is happening here. The function is filtering the MyModel objects to find all instances where name='Frank'. The call to values() will return a ValuesQuerySet which will only contain the name field of all found MyModel instances.
mockme/tests.py
import mock
from django.test import TestCase
from mocktest.mockme.models import MyModel
from mocktest.mockme.functions import chained_query
from mock_django.query import QuerySetMock
class SimpleTest(TestCase):
def test_chained_query(self):
# without mocked queryset the result should be 0
result = chained_query()
self.assertEquals(result.count(), 0)
# now try to mock values().filter() and reeturn
# one 'Frank'.
qsm = QuerySetMock(MyModel, MyModel(name='Frank'))
with mock.patch('django.db.models.Manager.filter', qsm):
result = chained_query()
self.assertEquals(result.count(), 1)
The first assertEquals will evaluate as successful. No instances are returned since the model Manager is not mocked yet. When the second assertEquals is called I expect result to contain the MyModel instance I added as return value to the QuerySetMock:
qsm = QuerySetMock(MyModel, MyModel(name='Frank'))
I mocked the filter() method and not the values() method since I found it'll be the last evaluated call, though I am not sure.
The test will fail because the second result variable won't contain any MyModel instances.
To be sure that the filter() method is really mocked I added a "debug print" statement:
from django.db import models
print models.Manager.filter
which returned:
<SharedMock name='mock.iterator' id='4514208912'>
What am I doing wrong?
Try this:
import mock
from mocktest.mockme.models import MyModel
class SimpleTest(TestCase):
def test_chained_query(self):
my_model_value_mock = mock.patch(MyModel.objects, 'value')
my_model_value_mock.return_value.filter.return_value.count.return_value = 10000
self.assertTrue(my_model_value_mock.return_value.filter.return_value.count.called)
#Gin's answer got me most of the way there, but in my case I'm patching MyModel.objects, and the query I'm mocking looks like this:
MyModel.objects.filter(arg1=user, arg2=something_else).order_by('-something').first()
so this worked for me:
#patch('MyModel.objects')
def test_a_function(mock, a_fixture):
mock.filter.return_value.order_by.return_value.first.return_value = a_fixture
result = the_func_im_testing(arg1, arg2)
assert result == 'value'
Also, the order of the patched attributes matters, and must match the order you're calling them within the tested function.

Python import problem with Django management commands

For whatever reason, when I was new to Python and Django, I wrote some import statements like this at the top of a models.py file:
from django.contrib import auth
And I'd use it like this:
class MyModel(models.Model):
user = models.ForeignKey(auth.models.User)
# ...
This worked fine. A long time later, I wrote a custom management command, and it would do this:
from myapp.models import MyModel
When I ran my custom command (python manage.py my_command) this would result in Python complaining that the module auth had no attribute models on the line declaring the ForeignKey in models.py.
To work around this problem, I changed my models.py to the more usual:
from django.contrib.auth.models import User
class MyModel(models.Model):
user = models.ForeignKey(User)
# ...
Can someone explain to me what I am missing? Is there something different in the environment when you run a management command? Or was I just doing it wrong the whole time? Thanks!
Edit: Following dmitko's hunch about circular imports, here are the imports used in my models.py file. I'm showing the original import of auth commented out, along with the only model that has a foreign key to the auth user model:
import datetime
from django.db import models
# from django.contrib import auth
from django.contrib.auth.models import User
class UserLastVisit(models.Model):
# user = models.ForeignKey(auth.models.User, unique=True)
# ^^^^^^^^^^^^^^^^
# after adding mgmt command, error occurred here; change to the line below
user = models.ForeignKey(User, unique=True)
last_visit = models.DateTimeField(db_index=True)
And here are the imports of the management command that uncovered the problem:
import datetime
from django.core.management.base import NoArgsCommand
from core.models import UserLastVisit, AnonLastVisit, Statistic
Was this setting up a circular import type situation?
If some random module ever imports module x.y.z, then a later person who imports just x.y will see a z in the x.y namespace.
The reason this happens is that import x.y.z is actually three import statements in one. It works something like this:
x = __internal_import('x')
x.y = __internal_import('x/y')
x.y.z = __internal_import('x/y/z')
Next time someone does __internal_import('x/y'), they'll get the same object, because python is smart enough not to import the same one twice. That object already has its z member assigned to the z module.
In your full app, probably you had a module that did import django.contrib.auth.models. But your minimal standalone program didn't import that module, so the name was never assigned.
(Note: there's no such thing as __internal_import. It's just an illustration. The real function has some other name that you would have to look up.)
I guess that if you do from django.contrib import auth that means you're importing auth package as a module and what it exports is driven by __init__.py in the auth folder:
>>> from django.contrib import auth
>>> dir(auth)
['BACKEND_SESSION_KEY', 'ImproperlyConfigured', 'REDIRECT_FIELD_NAME', 'SESSION_
KEY', '__builtins__', '__doc__', '__file__', '__name__', '__path__', 'authentica
te', 'datetime', 'get_backends', 'get_user', 'import_module', 'load_backend', 'l
ogin', 'logout']
You can check __init__.py in django\contrib\auth and see the same function list. When you import from django.contrib.auth.models import User that means that you're importing a submodule from the auth package and it works.
BTW. I was unable to use auth.models.User in any case - whether I run from console or from my django app.
It's hard to say exactly what's going on without seeing the new manage.py command that you added. However, I often see the " has no attribute " in cases with circular imports, and it's almost always fixed by changing the module-level imports to function- or class-level imports, as you did here. You might check if anything like that is going on here.

Categories