IntegrityError in Django testcase - django

I am suspecting that transaction.atomic() does not commit my instance to the database during testing. The problem might comes from multiple databases
from django.db import models
from model_utils.models import TimeStampedModel
class PIIField(TimeStampedModel):
"""
Personal Information Identifier configuration model
This model is M2M on `Plan`
"""
name = models.CharField(max_length=30)
detail = models.CharField(max_length=50, null=True, blank=True)
order = models.SmallIntegerField(unique=True)
def __str__(self):
return self.name
class Plan(timestamp.Model, models.Model):
class Meta:
db_table = "catalog_plans"
product = models.ForeignKey(Product,
on_delete=models.CASCADE,
related_name="plans")
coverages = models.ManyToManyField(Coverage, through="PlanCoverage")
code = models.TextField(null=True)
name = models.TextField()
pii_fields = models.ManyToManyField(PIIField, related_name='plans', related_query_name='plan')
Here is my tests.py
from django.db import transaction
from django.test import TransactionTestCase
from model_mommy import mommy
from rest_framework import status
from rest_framework.reverse import reverse
from rest_framework.test import APIClient
from catalog.models import Plan
from pii_fields.models import PIIField
class PIIFieldTestCase(TransactionTestCase):
databases = {'default', 'product', 'customer'}
def setUp(self) -> None:
with transaction.atomic():
self.plan = mommy.make(Plan, code='88', name='Non risky life') # single `plan` with no` pii_fields` attached
self.field_1 = mommy.make(PIIField, name='first_name', detail='text box', order=1)
self.field_2 = mommy.make(PIIField, name='last_name', detail='text box', order=2)
self.field_3 = mommy.make(PIIField, name='weight', detail='real number', order=3)
self.field_4 = mommy.make(PIIField, name='nationality', detail='drop down', order=4)
self.plan.pii_fields.set([self.field_1, self.field_2, self.field_3, self.field_4])
def test_get(self):
"""
Get the endpoint and see the payload sample
:return:
"""
client = APIClient()
url = reverse('api:pii_field-detail', args=[self.plan.id])
res = client.get(url)
self.assertEqual(status.HTTP_200_OK, res.status_code)
Error:
django.db.utils.IntegrityError: insert or update on table "catalog_plans_pii_fields" violates foreign key constraint "catalog_plans_pii_fi_piifield_id_58130345_fk_pii_field"
DETAIL: Key (piifield_id)=(1) is not present in table "pii_fields_piifield".
Question:
How to test my database and viewsets?

I have to use another syntax to add my records. I had tried bulk_create, but it does not work
self.plan.pii_fields.create(name='first_name', detail='text box', order=1)
self.plan.pii_fields.create(name='last_name', detail='text box', order=2)
self.plan.pii_fields.create(name='weight', detail='real number', order=3)
self.plan.pii_fields.create(name='nationality', detail='drop down', order=4)

Related

ValueError: Cannot assign "'1'": "Post.user" must be a "User" instance

I am doing a group project for a bootcamp and we just started Django for the back-end. We also are using React for front-end. Our project is basically a knockoff reddit.
We have a User model:
`from django.db import models
class User(models.Model):
firstname = models.CharField(max_length=100)
lastname = models.CharField(max_length=100)
email = models.CharField(max_length=100, unique=True)
username = models.CharField(max_length=32, unique=True)
password = models.CharField(max_length=255)
def __str__(self):
return '%s' % (self.username)`
and a Post model:
`from django.db import models
from auth_api.models import User
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
title = models.CharField(max_length=255)
formBody = models.TextField(null=True, blank=True)
imageURL = models.CharField(max_length=200, null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)`
Our Post Serializers(pretty unfamiliar with this):
`from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.ModelSerializer):
user = serializers.CharField(required=True)
class Meta:
model = Post
fields = ('id', 'user', 'title', 'formBody', 'imageURL', 'created',)`
And our Post Views:
`from django.shortcuts import render
from rest_framework import generics
from .serializers import PostSerializer
from .models import Post
from auth_api.models import User
class PostList(generics.ListCreateAPIView):
queryset = Post.objects.all().order_by('id')
serializer_class = PostSerializer
class PostDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Post.objects.all().order_by('id')
serializer_class = PostSerializer`
The idea was when a user created a post their info would be saved with the post so that way when we display the post we could say who created at. Also we could have a user profile that could see all of their posts. I assumed that what would happen is the user info would get saved inside a object in the user column, but the first way we tried only saved the userID and we couldn't access any of the users info. The second way(what we have now) keeps giving us this error: ValueError: Cannot assign "'1'": "Post.user" must be a "User" instance.The 1 is the userID that we pass in from the frontend of the user that created the post. I am unsure of where to go from here and have been stuck for a while on this. Hopefully I provided enough info

Django unit testing FileField and ImageField using ContentFile

I am using Django 3.2
I have a model like this:
Foo class
class Foo(models.Model):
name = models.CharField(max_length=124)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
description = models.TextField()
bio = models.TextField()
attachment = models.FileField()
photo = models.ImageField()
recorded_date = models.DateField()
time_of_day = models.TimeField()
timestamp = models.DateTimeField()
duration = models.DurationField()
website = models.URLField()
pseudo_array = models.CharField(max_length=256)
pseudo_tags = models.CharField(max_length=128)
Snippet of Unit test
import glob
import os
import json
from datetime import datetime, timedelta
from django.utils.timezone import make_aware
from model_bakery import baker
from django.test import TestCase
from django.core.exceptions import ValidationError
from django.core.files.base import ContentFile
image_mock =ContentFile(b"R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==", name='photo.png')
file_mock = ContentFile("Some random text", name='archive.txt')
class TestModels(TestCase):
def setUp(self):
current_timestamp = datetime.now(timezone.utc)
self.foo = Foo.objects.create( name='Accepted Object',bio='For accepted testing',
owner=self.pattysmith,
description='Object for accepted testing',
attachment = file_mock,
photo = image_mock,
recorded_date = datetime.strptime('20200101','%Y%m%d'),
time_of_day = datetime.strptime('10:00','%H:%M'),
timestamp = make_aware(timezone.now().strptime('20200101 10:00','%Y%m%d %H:%M')),
duration = timedelta(days=20, hours=10),
website = 'https://www.accepted.com',
moderation_status=1,
pseudo_tags='approved,nice, accepted'
)
def tearDown(self):
Foo.objects.all().delete()
User.objects.all().delete()
for f in glob.glob("*.png"):
os.remove(f)
for f in glob.glob("*.txt"):
os.remove(f)
def test_change_moderated_char_field(self):
self.foo.name='My new name'
self.foo.save(update_fields=['name'])
# test for number of changed fields ...
When I run the test test_change_moderated_char_field I see that the file and image field names have changed - looks like Django is auto-generating the file names.
Here is what my console printout looks like:
moderated_field_current_field_value: 'My new name' != 'Accepted Object' for moderated field: name
moderated_field_current_field_value: /path/to/archive_Fo8NWLI.txt != /path/to/archive.txt for moderated field: attachment
moderated_field_current_field_value: /path/to/photo_mVEyGtI.png != /path/to/photo.png for moderated field: photo
######### changed_fields: ["name", "attachment", "photo"] #####
I am currently accessing the name of the file/image by accessing the path attribute on the field. How do I get the name of the file when it is actually uploaded (since some name mangling seems to be taking place?

Django foreign key issue with django-import-export library (IntegrityError at /import/ FOREIGN KEY constraint failed)

I'm relatively new to Django and not an advanced programmer, so please pardon my ignorance.
What is working:
I have a Django application that uses one main model which connects to two secondary models with foreign keys. The application can correctly create companies from template and from admin, and can correctly display the "niche" drop-down field using a foreign key to the Category model and can correctly display the images using a foreign key from the CompanyImage model.
What is not working:
The django-import-export library can correctly import an XLS document from front end and from admin, but ONLY if I disable the Category and CompanyImage model that are relying on foreign keys. The library does import correctly with the default user=models.ForeignKey(User) in my main Company model, but the foreign keys that connect to the secondary models are causing a foreign key error: IntegrityError at /import/ FOREIGN KEY constraint failed.
What I need
The XLS sheet I am importing does not import the fields that use a foreign key, so I would like to disable those fields to avoid the foreign key error. It would be nice to import a niche/category field, but I can do without.
What I've tried
I've spent two days trying to fix this problem.
I've tried reading the django-import-export documentation.
I've tried adding list_filter and exclude in class Meta for the Resource model.
I've read through Dealing with import of foreignKeys in django-import-export.
I've read through foreign key in django-import-export.
I would be very grateful someone can help steer me in the right direction. Thank you.
Models.py
from django.db import models
from django.contrib.auth.models import User
from phonenumber_field.modelfields import PhoneNumberField
#had to use pip install django-phone-verify==0.1.1
from django.utils import timezone
import uuid
from django.template.defaultfilters import slugify
class Category(models.Model):
kind = models.CharField(verbose_name='Business Type',max_length=100,blank=True,)
class Meta:
verbose_name_plural = "Categories"
def __str__(self):
return self.kind
class Company(models.Model):
#BASIC
title = models.CharField(verbose_name='company name',max_length=100,blank=True)
contact = models.CharField(verbose_name='director',max_length=100,blank=True)
phone_number = PhoneNumberField(blank=True)
email = models.EmailField(max_length=200,blank=True)
email_host = models.CharField(max_length=200,blank=True)
website = models.URLField(max_length=200,blank=True)
facebook = models.URLField(max_length=200,blank=True)
memo = models.TextField(blank=True)
niche = models.ForeignKey(Category, default=0000,on_delete=models.SET_DEFAULT)
#UPLOADS
profile_picture = models.ImageField(upload_to='prospects/images/', blank=True)
image = models.ImageField(upload_to='prospects/images/', blank=True)
file = models.FileField(upload_to='prospects/uploads', blank=True)
#TIME
date = models.DateField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
datecompleted = models.DateTimeField(null=True, blank=True) #null for datetime object
#BOOLIANS
important = models.BooleanField(default=False)
cold = models.BooleanField(default=False, verbose_name='these are cold leads')
warm = models.BooleanField(default=False, verbose_name='these are warm leads')
hot = models.BooleanField(default=False, verbose_name='these are hot leads')
#USER
user = models.ForeignKey(User, on_delete=models.CASCADE,null=True,blank=True)
#TEST MODEL
decimal = models.DecimalField(max_digits=5, decimal_places=2, blank=True, default=00.00)
integer = models.IntegerField(blank=True, default=0000)
positive_int = models.PositiveIntegerField(null=True, blank=True, default=0000)
positive_small_int = models.PositiveSmallIntegerField(null=True, blank=True, default=0000)
#ADMIN CONSOLE
class Meta:
verbose_name_plural = "Companies"
def __str__(self):
if self.title == "":
print('empty string')
return "No Name"
elif type(self.title) == str:
return self.title
else:
return "No Name"
# this makes the title appear in admin console instead of object number
class CompanyImage(models.Model):
company = models.ForeignKey(Company, default=None, on_delete=models.CASCADE)
image = models.FileField(upload_to = 'prospects/images/',blank=True)
def __str__(self):
return self.company.title
resource.py
from import_export import resources
# from import_export import fields
from import_export.fields import Field
from import_export.fields import widgets
from .models import Company
from django.utils.encoding import force_str, smart_str
# The following widget is to fix an issue with import-export module where if i import any number from an xls file, it imports as a float with a trailing ,0
#could keep it a number and use trunc function to take away decimal but will make string
class DecimalWidget(widgets.NumberWidget):
def clean(self, value, row=None, *args, **kwargs):
print()
print(f"type of value is {type(value)}")
print()
if self.is_empty(value):
return ""
elif type(value) == float:
new_string = force_str(value)
seperator = '.'
new_string_witout_0 = new_string.split(seperator, 1)[0]
print()
print(f"the new type of value is {type(value)}")
print(f"the new value is {value}")
print()
return new_string_witout_0
else:
print("Aborting! it's not a float or empty string. will just return it as it is.")
return value
print()
print(f"type of value is {type(value)}")
print(f" the value returned is {value}")
print()
class CompanyResource(resources.ModelResource):
title = Field(attribute='title', column_name='name',widget=DecimalWidget())
contact = Field(attribute='contact', column_name='contact',widget=DecimalWidget())
phone_number = Field(attribute='phone_number', column_name='phone',widget=DecimalWidget())
# niche = Field(attribute='niche', column_name='niche',widget=DecimalWidget())
class Meta:
model = Company
exclude = ('niche')
fields = ('id','title','contact','phone_number', 'email','email_host','website','facebook')
export_order = ['id','title','contact','phone_number', 'email','email_host','website','facebook']
# fields = ( 'id', 'weight' )
admin.py
from django.contrib import admin
from import_export.admin import ImportExportModelAdmin
from import_export.fields import Field
from import_export import resources
# from import_export import resources
from .models import Company,Category, CompanyImage
from.resources import CompanyResource
class CompanyResource(resources.ModelResource):
class Meta:
model = Company
class CompanyImageAdmin(admin.StackedInline):
model = CompanyImage
class CompanyAdmin(ImportExportModelAdmin):
resource_class = CompanyResource
inlines = [CompanyImageAdmin]
# Register your models here.
admin.site.register(Category)
admin.site.register(Company,CompanyAdmin)
#admin.register(CompanyImage)
class CompanyImageAdmin(admin.ModelAdmin):
pass
views.py
def importcompanies(request):
if request.method == 'GET':
return render(request, 'prospects/import.html')
else:
file_format = request.POST['file-format']
company_resource = CompanyResource()
dataset = Dataset()
new_companies = request.FILES['myfile']
if file_format == 'CSV':
imported_data = dataset.load(new_companies.read().decode('utf-8'),format='csv')
result = company_resource.import_data(dataset, dry_run=True, raise_errors=True)
elif file_format == 'XLSX':
imported_data = dataset.load(new_companies.read(),format='xlsx')
result = company_resource.import_data(dataset, dry_run=True, raise_errors=True)
elif file_format == 'XLS':
imported_data = dataset.load(new_companies.read(),format='xls')
result = company_resource.import_data(dataset, dry_run=True, raise_errors=True)
if result.has_errors():
messages.error(request, 'Uh oh! Something went wrong...')
else:
# Import now
company_resource.import_data(dataset, dry_run=False)
messages.success(request, 'Your words were successfully imported')
return render(request, 'prospects/import.html')
You have CompanyResource defined in two places, so this could be the source of your problem. Remove the declaration from admin.py and see if that helps.
As you say, fields and exclude are used to define which model fields to import. fields is a whitelist, whilst exclude is a blacklist, so you shouldn't need both.
Set up a debugger (if you haven't already) and step through to find out what is going on (this can save days of effort).
If it is still not working, please update your answer and try to be specific about the nature of the issue (see how to ask).

Testing model - how to create records

according to https://docs.djangoproject.com/en/1.9/topics/testing/overview/ :
from django.test import TestCase
from myapp.models import Animal
class AnimalTestCase(TestCase):
def setUp(self):
Animal.objects.create(name="lion", sound="roar")
Animal.objects.create(name="cat", sound="meow")
def test_animals_can_speak(self):
"""Animals that can speak are correctly identified"""
lion = Animal.objects.get(name="lion")
cat = Animal.objects.get(name="cat")
self.assertEqual(lion.speak(), 'The lion says "roar"')
self.assertEqual(cat.speak(), 'The cat says "meow"')
I want to create setUp to my model class:
class Post(models.Model):
author = models.ForeignKey('auth.User')
tags = models.ManyToManyField('blogUserPlane.Tag')
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(
default=timezone.now)
published_date = models.DateTimeField(
blank=True, null=True)
This is my NOT WORKING setUp:
def setUp(self):
Post.objects.all().create(author=User(), tag=models.ManyToManyField('blogUserPlane.Tag'), title="title1", text="text1", created_date=None, published_date=None)
What is correct way to create records of model with ManyToManyField and ForeginKey?
If you want to enter a value in a ForeignKey or ManyToMany field ,you first need to import that value .
For example if you want to enter a value in author field ,
from django.contrib.auth.models import User
user = User.objects.get(username = 'your_username')
Post.objects.create(author = user)
To save M2M
Save some values in the link table
`tag = blogUserPlane.Tag()
...
tag.save()`
To save Foreign key
from django.contrib.auth.models import User
post = Post()
post.author = User
...
post.tags.add(tag)
post.save()

How do I import data using Django-Import-Export?

The documentation for django-import-export is a bit weak on how to configure the admin to import from a spreadsheet. Does anyone have a full example?
This is not a fully complete module. But you can understand how it should be.
resources.py file
from import_export import resources
from .models import edxUser
class edxUserResource(resources.ModelResource):
class Meta:
model = edxUser
#skip_unchanged = True
#report_skipped = True
#if you want to exclude any field from exporting
exclude = ('id','edx_anonymized_id')
fields = ('id', 'edx_id', 'edx_anonymized_id', 'edx_email', 'edx_name', 'time_created', 'created_by')
#Order of the export fields
export_order = ('edx_id', 'edx_email')
admin.py file
from import_export.admin import ImportExportModelAdmin
from django.contrib import admin
from .models import edxUser
from resources import edxUserResource
#admin.register(edxUser)
class edxUserAdmin(ImportExportModelAdmin):
resource_class = edxUserResource
models.py file
from __future__ import unicode_literals
from django.conf import settings
from django.db import models
class edxUser(models.Model):
edx_id = models.IntegerField('edX user id', blank=True, null=True)
edx_anonymized_id = models.IntegerField("edX anonymized user id", blank=True, null=True)
edx_email = models.EmailField('edx user email', max_length=75, blank=True)
edx_name = models.CharField('edx name', max_length=75, blank=True, null=True)
time_created = models.DateField('Created time', blank=True, null=True)
created_by = models.OneToOneField(settings.AUTH_USER_MODEL, null=True, blank=True)
def __unicode__(self):
return str(self.edx_id)
Here's how to do it, assuming that the column names in the spreadsheet are Title and Field one. This example assumes that the model instances will be created afresh every import (rather than being updated via a primary key).
from django.contrib import admin
from import_export.admin import ImportMixin
from import_export import resources, fields
from .models import MyModel
class MyModelResource(resources.ModelResource):
title = fields.Field(attribute='title',
column_name='Title')
field_one = fields.Field(attribute='field_one',
column_name='Field one')
def get_instance(self, instance_loader, row):
# Returning False prevents us from looking in the
# database for rows that already exist
return False
class Meta:
model = MyModel
fields = ('title', 'field_one')
export_order = fields
class MyModelAdmin(ImportMixin, admin.ModelAdmin):
resource_class = MyModelResource
admin.site.register(MyModel, MyModelAdmin)