I'm trying to write a test for my CreateView view in django. As part fo the test, I want to create a new object through a CreateView class, though I'm unsure how to save the object through tests.py.
models.py
class MyModel(models.Model):
name = models.CharField(
max_length = 50,
)
views.py
class MyCreateView(CreateView):
model = MyModel
tests.py
from myapp.views import MyCreateView
m = MyCreateView()
m.name = 'John Doe'
# save object here
Neither m.save() nor m.submit() will work. Any suggestions?
Please refer to Django docs: https://docs.djangoproject.com/en/4.0/topics/testing/
It is well documented and shows how to test views and models.
Related
I am just starting to use pytest and faker for testing
while trying to create text for a field in testing db the constraints are being ignored and i don't know how to fix it.
models.py
from django.db import models
# Create your models here.
class Note(models.Model):
body = models.TextField(null=True, blank=True, max_length=5)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.body[0:50]
factories.py
import factory
from faker import Faker
fake = Faker()
from mynotes_api.models import Note
class NoteFactory(factory.django.DjangoModelFactory):
class Meta:
model = Note
body = fake.text()
conftest.py
import pytest
from django.contrib.auth.models import User
from pytest_factoryboy import register
from tests.factories import NoteFactory
register(NoteFactory)
#pytest.fixture
def new_user1(db, note_factory):
note = note_factory.create()
return note
test_ex1.py
import pytest
def test_product(new_user1):
note = new_user1
print(note.body)
assert True
test output
the problem as visible in output is that the length of the text generated and being stored in the testing db is more than 5.
kindly guide me in this regard.
Iain Shelvington's advice to use a CharField is the main issue here. It will enforce the database constraint.
Django offers more types of validation that cannot be enforced by the database. These will not be checked on a model.save call:
Note that validators will not be run automatically when you save a
model, but if you are using a ModelForm, it will run your validators
on any fields that are included in your form.
See How validators are run.
In one celery task I need to create temporary table in database. In this article Daniel Roseman explained how to create one. But this solution does not work in Django 1.9. I tried to look into Django docs and Google but I was unable to find anything useful.
Code from mentioned article which worked in Django 1.8:
from django.db import models, cursor
from django.contrib.contenttypes.management import update_contenttypes
from django.core.management import call_command
class TempCustomerAddress(models.Model):
address = models.ForeignKey('accounts.Address')
legacy_id = models.CharField(max_length=12, unique=True)
class Meta:
app_label = 'utils'
class Command(NoArgsCommand):
def handle_noargs(self, **options):
models.register_models('utils', TempCustomerAddress)
models.signals.post_syncdb.disconnect(update_contenttypes)
call_command('syncdb')
# ... do importing and stuff referring to TempCustomerAddress ...
cursor = connection.cursor()
cursor.execute('DROP TABLE `utils_tempcustomeraddress`')
In django 1.9 you actually don't need to register anything. You just create model the same way as in models.py and that's it. You only need to make sure that it is not in models.py file because than it will be permanent model.
This example assumes that you already ran all migrations.
from django.db import models, cursor
from django.contrib.contenttypes.management import update_contenttypes
from django.core.management import call_command
class TempCustomerAddress(models.Model):
address = models.ForeignKey('accounts.Address')
legacy_id = models.CharField(max_length=12, unique=True)
class Meta:
app_label = 'utils'
class Command(NoArgsCommand):
def handle_noargs(self, **options):
with connection.cursor() as cursor:
cursor.execute('DROP TABLE IF EXISTS utils_tempcustomeraddress')
cursor.execute('''
CREATE TABLE utils_tempcustomeraddress (
id INTEGER PRIMARY KEY NOT NULL,
address_id REFERENCES accounts_address (id),
legacy_id VARCHAR(12) UNIQUE
);
'''
# ... do importing and stuff referring to TempCustomerAddress ...
cursor.execute('DROP TABLE `utils_tempcustomeraddress`')
I needed to create a temporary model derived from a "permanent" model, and use temporary table storage to avoid polluting the tables of the permanent one. After a lot of poking around including an article relating to Django 0.96, some newer material it points to for Django 1.2 and some old material based on migration tech integrated into Django I finally came up with a recipe that works with Django 2.0.
First, I needed to explicitly specify the database table name using the Meta:
model_name = re.sub('[#.]', '_', 'some_string')
class Meta:
app_label = original_model._meta.app_label
#
# Use the explicit name for the database table.
#
db_table = '"' + model_name.lower() + '"'
Then I created the Model class by copying what I needed from the original:
attr = {'__module__': __name__, 'Meta': Meta}
local_fields = [field.name for field in original_model._meta.local_fields]
for field in original_model._meta.fields:
#
# Clone only the fields which we need, not forgetting that we only
# want one primary key.
#
clone = field.clone()
if field.name in local_fields:
local_fields.remove(field.name)
else:
clone.primary_key = False
if not isinstance(field, (db_models.AutoField, db_models.OneToOneField, db_models.ManyToManyField)):
attr[field.name] = clone
new_model = type(model_name, (db_models.Model,), attr)
The hard part was tracking down how to create the new table for the model. Once found, the answer was simple:
from django.db import connection
with connection.schema_editor() as schema_editor:
schema_editor.create_model(new_model)
I can't seem to make django-autocomplete-light work with the django contrib user model. Always get exception 'choices should be a queryset'
This is my autocomplete class (defined in autocomplete_ligh_registry.py):
import autocomplete_light
from django.contrib.auth.models import User
class UserAutocomplete(autocomplete_light.AutocompleteModelBase):
search_fields = ['first_name']
model = User
autocomplete_light.register(UserAutocomplete)
my form (in forms.py):
class TransactionForm(forms.Form):
mymodel = forms.ModelChoiceField( required=True,
queryset=User.objects.all() ,
widget=autocomplete_light.ChoiceWidget('UserAutocomplete'))
When trying to render with {{form}}, it throws an exception: choices should be a queryset:
stack:
/home/prj/docs/projectos/.../src/autocomplete-light/autocomplete_light/widgets.py in render
choices = autocomplete.choices_for_values() ...
▶ Local vars
/home/prj/docs/projectos/.../src/autocomplete-light/autocomplete_light/autocomplete/model.py in choices_for_values
assert self.choices is not None, 'choices should be a queryset'
This is django 1.6 running in development. I have Users created.
django-autocomplete-light works ok with an autocompleteListBase, e.g.:
class OsAutocomplete(autocomplete_light.AutocompleteListBase):
choices = ['Linux', 'BSD', 'Minix']
autocomplete_light.register(OsAutocomplete)
so urls.py are including the registry, urls are registered and javascript is being loaded.
Following these docs:
http://django-autocomplete-light.readthedocs.org/en/latest/index.html#tutorial
Any pointers?
Thanks!
Hmm, got it... Docs don't mention but it needs choices to be explicitly defined on the autocomplete class.
class UserAutocomplete(autocomplete_light.AutocompleteModelBase):
search_fields = ['email']
choices = User.objects.all()
model = User
autocomplete_light.register(UserAutocomplete)
I am developing an multilingual application using Django.
One part is to select the type of something using the ContentType API.
As describe in the doc, the ContentType object name is extracted from the verbose_name.
In my case the verbose_name is translated using xgettext_lazy but as it is copyied in the database during the syncdb, there is no translation for ContentType, the verbose_name is not translated.
I would like to be able to change the way the foreign key is displayed in a form.
Do you have any idea of how I can do that ?
Cheers,
Natim
You need to use ugettext_lazy instead of ugettext, and it's not stored in the database, but it's on some .po files. For instance:
from django.utils.translation import ugettext_lazy as _
class Event(models.Model):
...
class Meta:
verbose_name = _(u'Event')
verbose_name_plural = _(u'Events')
For code blocks that are loaded on import time, you need to use ugettext_lazy, and for those that are loaded on execution time, you need ugettext. Once you have that, you just need to do a "python manage.py makemessages" and "python manage.py compilemessages"
Finally here is the solution I found :
def content_type_choices(**kwargs):
content_types = []
for content_type in ContentType.objects.filter(**kwargs):
content_types.append((content_type.pk, content_type.model_class()._meta.verbose_name))
return content_types
LIMIT_CHOICES_TO = {'model__startswith': 'pageapp_'}
class PageWAForm(forms.ModelForm):
app_page_type = forms.ModelChoiceField(queryset=ContentType.objects.filter(**LIMIT_CHOICES_TO),
empty_label=None)
def __init__(self, *args, **kwargs):
super(PageWAForm, self).__init__(*args, **kwargs)
self.fields['app_page_type'].choices = content_type_choices(**LIMIT_CHOICES_TO)
Not sure what is causing this error. Please help
NameError at /dash/
name 'Business' is not defined
Exception Location: /home/src/common/models.py in ImageBank, line 38
Here's the model:
class Business(models.Model):
business_type = models.ManyToManyField(BusinessType)
business_service_type = models.ManyToManyField(ServiceType)
establishment_type = models.ForeignKey(EstablishmentType)
logo = models.ForeignKey(ImageBank)
phone = PhoneNumberField()
address = models.ForeignKey(Address)
website = models.URLField()
name = models.CharField(max_length=64)
def __unicode__(self):
return self.name
The View:
def dashview(request):
coupon = Coupon.objects.filter()
bdnspk = request.user.id
user = request.user.username
bdns = Business.objects.values('name').get(id=bdnspk)
context = {
'coupon':coupon,
'bdns':bdns,
'user':user
}
return render_to_response(
'dash/dash.html',
{},
context,
context_instance = RequestContext(request),
)
EDIT: my models is located in /home/src/common/models.py but my django app is in /home/proj/site/ How do I import that?
ImageBank model:
class ImageBank(models.Model):
business = models.ForeignKey('Business')
image = models.ImageField(upload_to="images/bank")
def url(self):
return self.image.url
Please look at your error: Exception Location: /home/src/common/models.py in ImageBank, line 38 the problem exists in the ImageBank class, which you also seem to be using a ForeignKey reference to in the logo field.
I'm assuming that what the issue is is that you are referencing Business before it is defined as something like a ForeignKey reference inside a field in ImageBank. If this is the case, is ImageBank defined before the Business model inside your models.py? Because doing so will throw this error. The proper way of doing circular ForeignKey references would be to enforce a single ForeignKey with a unique constraint.
Django has this concept built in as a type of field called a OnetoOne field. Have you looked into using a OnetoOne field? See: http://docs.djangoproject.com/en/dev/ref/models/fields/#onetoonefield
Did you import the models in the view? Something like:
from models import Business
at the beginning of the view file
You forgot to import the model in the view, or you're referring to it incorrectly.
If that model is in an app you wrote:
Make sure that the app is listed in INSTALLED_APPS in your settings.py
#settings.py
INSTALLED_APPS = (
'django....',
... more defaults ...,
'myproject.appname',
)
and at the top of your views
#views.py
from appname.models import Business
#or import all models from that app
from appname.models import *
You are making things a lot more complicated on yourself by having your models.py in a strange unrelated location.
Models can only be imported from python modules so you'll need to make sure that your models.py is in a directory that is a python module and that it is on the python path.
You'll be a whole lot better of just putting your models into an app in your project rather than trying to do something like you are.
More or less you're working against the grain and python is a lot nicer if you work with the grain.