AssertionError in unit testing - python-3.x

while I was writing tests I got that error
assert response.status_code == status.HTTP_201_CREATED
E AssertionError: assert 415 == 201
E -415
E +201
models.py
class Product(TimeStamp):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
color = models.ManyToManyField(ColorOfProduct)
available = models.BooleanField(default=True)
image = models.ImageField(upload_to='products/%Y/%m/%d', null=True, blank=True)
slug = models.SlugField(max_length=100, unique=True, blank=True)
def __str__(self):
return f'{self.category} - {self.name}'
tests.py looks like this
class ProductTest(APITestCase):
def post_product(self, category, name, brand_name, color, available):
url = reverse(ProductView.name)
print(url)
data = {
'category': category,
'name': name,
'brand_name': brand_name,
'color': color,
'available': available,
}
response = self.client.post(url, data, format='json')
return response
def test_post_and_get_product(self):
new_category_name = 'Hewlet Packard'
new_product_name = 'HP Zenbook'
new_brand_name = 'HP'
new_color = 'black'
new_available = True
response = self.post_product(
new_category_name,
new_product_name,
new_brand_name,
new_color,
new_available,
)
assert response.status_code == status.HTTP_201_CREATED
assert Product.objects.count() == 1
assert Product.objects.get().name == new_product_name
I am indicating media type but when I check it throws error "Unsupported media type"
how can I solve this issue?

The reason might be django's default test format is application/octet-stream. You need to use json.dumps() before calling your test method. In your post_product method, just change:
response = self.client.post(url, data, format='json')
to
import json
response = self.client.post(url, json.dumps(data), content_type='application/json')

Related

Django Rest Framework Field Validation Issue

My subscription view is located inside of UserViewSet. I'm wondering why I'm getting
IntegrityError at /api/users/1/subscribe/
new row for relation "users_subscription" violates check constraint "prevent_self_subscription"
DETAIL: Failing row contains (11, 1, 1).
instead of proper json answer. Somehow SubscriptionSerializer field validation doesnt wish to work. Any thoughts?
models.py
class Subscription(models.Model):
user = models.ForeignKey(
User, related_name='subscriber',
on_delete=models.CASCADE)
author = models.ForeignKey(
User, related_name='subscribing',
on_delete=models.CASCADE)
class Meta:
constraints = [
models.UniqueConstraint(
fields=('user', 'author'),
name='unique_subscription'
),
models.CheckConstraint(
check=~models.Q(user=models.F('author')),
name='prevent_self_subscription'
)
]
serializers.py
class SubscriptionSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(
read_only=True, default=serializers.CurrentUserDefault())
class Meta:
model = models.Subscription
fields = ('author', 'user', )
validators = [
serializers.UniqueTogetherValidator(
queryset=models.Subscription.objects.all(),
fields=['author', 'user', ]
)
]
def create(self, validated_data):
return models.Subscription.objects.create(
user=self.context.get('request').user, **validated_data)
def validate_subscribing(self, value):
if self.context.get('request').user == value:
raise serializers.ValidationError(
'You cant subscribe to yourself!')
return value
views.py
#action(['post'], detail=True)
#permission_classes(permissions.IsAuthenticated)
def subscribe(self, request, *args, **kwargs):
author = get_object_or_404(models.User, id=kwargs['id'])
data = request.data.copy()
data.update({'author': author.id})
serializer = serializers.SubscriptionSerializer(
data=data, context={'request': request})
if request.method == 'POST':
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(
status=status.HTTP_201_CREATED,
data=self.get_serializer(author).data)
It was the wrong method name, field name is 'author' and method was validate_subscribing() instead of validate_author().

EcomUser() got an unexpected keyword argument 'mobile_number'

I'm trying to create a user and link it with the user profile. But here is the problem.
This is my profile model
class EcomUser(BaseModelMixin):
profile = models.OneToOneField(
settings.AUTH_USER_MODEL, unique=True, blank=True, null=True,
on_delete=models.CASCADE)
username = models.CharField(max_length=100, blank=True)
email = models.CharField(max_length=100, blank=True, null=True)
name = models.CharField(max_length=100, blank=True)
referral_code = models.CharField(max_length=10, blank=True)
signinId = models.CharField(max_length=20, blank=True)
GOOGLE = 'GOOGLE'
APPLE = 'APPLE'
MOBILE_NUMBER = 'mobile_number'
LOGIN_TYPES = (
(GOOGLE, 'GOOGLE'),
(APPLE, 'APPLE'),
(MOBILE_NUMBER, 'mobile_number')
)
loginType = models.CharField(max_length=15, choices=LOGIN_TYPES, default=MOBILE_NUMBER)
class Meta:
db_table = "ecom_user"
My user model:
class User(AbstractBaseUser, BaseModelMixin):
email = models.EmailField(
verbose_name='email_address',
max_length=255,
blank=True,
null=True
)
username = models.CharField(max_length=128, null=True,unique=True)
fullName = models.CharField(max_length=256, blank=True)
mobile_number = models.CharField(max_length=15, blank=True,unique=True)
is_superuser = models.BooleanField(default=False)
class Meta:
db_table = "users"
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = [] # Email & Password are required by default.
def get_full_name(self):
# The user is identified by their email address
return self.fullName
def __str__(self): # __unicode__ on Python 2
return self.email
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True
def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True
objects = UserManager()
and serializer.py
class UserRegisterSerializer(serializers.Serializer):
"""Normal serializer which accepts mobile number"""
mobile_number = serializers.CharField(max_length=10, min_length=10, required=True)
referral_code = serializers.CharField(max_length=10, min_length=10, required=True)
email = serializers.EmailField(max_length=255)
def validate_referral_code(self, referral_code):
if not re.match(r"^[\w]{10}$", referral_code):
raise serializers.ValidationError("Wrong Referral Code")
return referral_code
def validate_mobile_number(self, mobile_number):
if not re.match(r"^[\d]{10}$", mobile_number):
raise serializers.ValidationError("Please pass proper mobile number")
return mobile_number
class EcomUserSerializer(serializers.ModelSerializer):
profile_id = serializers.IntegerField(required=True)
mobile_number = serializers.CharField(max_length=10, min_length=10, required=True)
referral_code = serializers.CharField(max_length=10, min_length=10, required=True)
class Meta:
model = EcomUser
fields= ('profile_id','referral_code', 'mobile_number', 'loginType', 'signinId')
def create(self, data):
instance = EcomUser.objects.create(**data)
return instance
views.py
#api_view(['POST'])
def registration(request):
try:
local_reponse = {
"status": False,
"message": "",
"code": status.HTTP_400_BAD_REQUEST,
"data": []
}
data = request.data
print('1')
mobile_number = data.get('mobile_number', None)
referralCode = data.get('referralCode', '')
LoginType = data.get('LoginType')
signinId = data.get('signinId', '')
email = data.get('email', '')
print(f'{LoginType} --- {EcomUser.MOBILE_NUMBER}')
if LoginType == "mobile_number":
print('2')
exist_user = User.objects.filter(mobile_number=mobile_number).count()
if exist_user:
print('3')
local_reponse["message"] = msg.ALREADY_EXIST
return Response(local_reponse)
try:
print('not found')
registerd_user = User.objects.get(mobile_number=mobile_number)
print(f'{registerd_user} -- check')
if registerd_user:
print('register')
ecom = EcomUser.objects.get(profile_id=registerd_user.id)
if ecom:
local_reponse["message"] = msg.ALREADY_EXIST
except User.DoesNotExist:
print('4')
user = User.objects.create_ecom_user(mobile_number=mobile_number)
user.save()
print('is user create?')
if user:
print('yes')
info = {}
info['profile_id'] = user.id
info['referral_code'] = referralCode
info['mobile_number'] = mobile_number
info['LoginType'] = LoginType
info['signinId'] = signinId
ecom_serializer = EcomUserSerializer(data=info)
if not ecom_serializer.is_valid():
print('5')
local_reponse["message"] = ecom_serializer.error_messages
local_reponse["status"] = False,
return Response(local_reponse)
print(f'linked profile 1 ----' )
ecomuser_data = ecom_serializer.save()
print(f'linked profile 2 ---- ' )
ecomuser_id = ecomuser_data.id
print('linked profile 3' )
ecomuser = EcomUser.objects.get(id=ecomuser_id)
print('linked profile 4' )
ecom_serializer = EcomUserSerializer(ecomuser)
print('linked profile 5' )
context = ecom_serializer.data
print('linked profile 6' )
local_reponse = {
"status": True,
"message": msg.CREATED,
"code": status.HTTP_201_CREATED,
"data": context
}
print('linked profile 7' )
return Response(local_reponse)
except Exception as e:
print('7')
local_reponse["message"] = str(e)
return Response(local_reponse)
When I try to run API on postman. It showed "EcomUser() got an unexpected keyword argument 'mobile_number'" and the output in the terminal show below:
1
mobile_number --- mobile_number
2
not found
4
is user create?
yes
linked profile 1 ----
7
The user is created but can not link to the profile, any one can help?
There's no mobile_number field in your EcomUser model. Thus when you call
ecomuser_data = ecom_serializer.save()
after adding
info['mobile_number'] = mobile_number
if fails, leaving you with the user and no profile. I guess is_valid() is only checking the fields it wants, not detecting the surplus, but the extra field causes the save to fail with the error shown. You've already added the field when creating the user, so you don't need it in the profile, so removing that second line should make things work.

Django Query data optimization

Recently, I watched Django and discovered the teaching videos of select_related and prefetch_related.
So I installed debug_toolbar and took a look at my website
I searched the database too many times on one page.
I must convert it to json and send it back to the front end
Can I still optimize?
Is this the only way to go?
Below is my model .
models.py
def get_upload_path(instance, filename):
return f'register/{instance.owner.job_number}/{filename}'
def image_upload_path(instance, filename):
date = datetime.datetime.strptime(instance.date, '%Y-%m-%d %H:%M:%S')
return f'image/{date.year}/{date.month}/{date.day}/{filename}'
class UserProfile(models.Model):
name = models.CharField(max_length=64)
department = models.CharField(max_length=32)
job_number = models.CharField(max_length=32, unique=True)
card_number = models.CharField(max_length=32, unique=True)
location = models.CharField(max_length=32)
date = models.DateTimeField()
class UserRegister(models.Model):
owner = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
date = models.DateTimeField()
feature = ArrayField(models.FloatField(null=True, blank=True), null=True, blank=True, size=512)
image = models.ImageField(upload_to=get_upload_path)
class Meta:
db_table = 'UserRegister'
class UserImage(models.Model):
owner = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
heat = models.CharField(max_length=8, blank=True, null=True)
date = models.DateTimeField()
image = models.ImageField(upload_to=image_upload_path)
punch_in = models.CharField(max_length=8, blank=True, null=True)
class Meta:
db_table = 'UserImage'
views.py
def get_db_data(db, page, limit, model):
data = []
paginator = Paginator(db, limit)
try:
page_object = paginator.page(page)
all_page = paginator.num_pages
for db_data in page_object:
images = []
for image in db_data.userregister_set.all():
try:
url = f'/static/uploads{image.image.url}'
except ValueError:
url = '/static/imgs/assets/none.jpg'
images.append({'id': image.id, 'url': url})
_ = {
'id': db_data.id,
'name': db_data.name,
'department': db_data.department,
'job_number': db_data.job_number,
'card_number': db_data.card_number,
'location': db_data.location,
'date': db_data.date.strftime('%Y-%m-%d %H:%M:%S'),
'images': images,
}
data.append(_)
result = {
'data': data,
'all_page': all_page,
'page': int(page)
}
return result
except EmptyPage as error:
# 該頁數沒資料。
return {'msg': 'Error'}
class Test(Views):
def get(self, request):
name = request.GET.get('name')
department = request.GET.get('department')
job_number = request.GET.get('job_number')
card_number = request.GET.get('card_number')
location = request.GET.get('location')
start_date = request.GET.get('start_date')
end_date = request.GET.get('end_date')
order = request.GET.get('order', default='DESC')
page = request.GET.get('page', default=1)
limit = request.GET.get('limit', default=25)
user_profile = UserProfile.objects.all().select_related()
if name:
user_profile = user_profile.filter(name=name)
if department:
user_profile = user_profile.filter(department=department_table.department)
if job_number:
user_profile = user_profile.filter(job_number=job_number)
if card_number:
user_profile = user_profile.filter(card_number=card_number)
if location:
user_profile = user_profile.filter(location=locations_table.location)
if start_date and end_date:
user_profile = user_profile.filter(date__range=[start_date, end_date])
if order == 'DESC':
data = get_db_data(db=user_profile.order_by('-date'), page=page, limit=limit, model='api')
else:
data = get_db_data(db=user_profile.order_by('date'), page=page, limit=limit, model='api')
response = JsonResponse(data)
# return response # This is the response I want to return, but now I need to use `debug_toolbar`, so I return an empty page to facilitate my reading `debug_toolbar`.
return render(request, 'User/test1.html')
return JsonResponse(data) is what I want to return, but now I need to use debug_toolbar, so I return to an empty page to facilitate reading debug_toolbar.
Use select_related() or prefetch_related().
Put the associated parameters into.
can speed up the ORM.

My Django model for video upload saying “ValueError”

I am trying to write a small blog where I can be uploading my videos for public download but am getting a server error message when I try click on any video for details. Below is the error that I'm getting when Debug is set to True
ValueError at /video/lagos-anthem/
Sample larger than population or is negative
Request Method: GET
Request URL: https://www.majestylink.com/video/lagos-anthem/
Django Version: 3.1.2
Exception Type: ValueError
Exception Value:
Sample larger than population or is negative
Exception Location: /home/majestyempire/.virtualenvs/majestyenv/lib/python3.7/random.py, line 321, in sample
Python Executable: /usr/local/bin/uwsgi
Python Version: 3.7.5
Python Path:
['/var/www',
'.',
'',
'/var/www',
'/home/majestyempire/.virtualenvs/majestyenv/lib/python37.zip',
'/home/majestyempire/.virtualenvs/majestyenv/lib/python3.7',
'/home/majestyempire/.virtualenvs/majestyenv/lib/python3.7/lib-dynload',
'/usr/lib/python3.7',
'/home/majestyempire/.virtualenvs/majestyenv/lib/python3.7/site-packages',
'/home/majestyempire/musicblog/myblog']
Server time: Sat, 28 Nov 2020 13:49:35 +0100
Below is my models.py
class Video(models.Model):
CATEGORY_CHOICES = (
('Music', 'Music'),
('Movies', 'Movies'),
)
artist = models.CharField(max_length=200, unique=True)
category = models.CharField(max_length=30, choices=CATEGORY_CHOICES, default='Music')
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(default='', blank=True, unique=True)
thumbnail = models.ImageField(blank=False)
video_file = models.FileField(default='')
uploaded_date = models.DateTimeField(default=timezone.now)
objects = PostManager()
class Meta:
ordering = ['-uploaded_date']
def save(self):
self.uploaded_date = timezone.now()
self.slug = slugify(self.title)
super(Video, self).save()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('video:detail', kwargs={'slug': self.slug})
This is the post_detail view
def post_detail(request, slug):
random_posts = random.sample(list(Video.objects.all()), 2)
vid = get_object_or_404(Video, slug=slug)
comments = Comment.objects.filter(post=vid)
is_liked = False
context = {
'Video': Video,
'vid': vid,
'random_posts': random_posts,
}
return render(request, 'video/video_detail.html', context)
def post_detail(request, slug):
a = Video.objects.all().count()
if a > 2:
random_posts = random.sample(list(Video.objects.all()), 2)
else:
random_posts = random.choices(list(Video.objects.all()))
vid = get_object_or_404(Video, slug=slug)
comments = Comment.objects.filter(post=vid)
is_liked = False
context = {
'Video': Video,
'vid': vid,
'random_posts': random_posts,
}
return render(request, 'video/video_detail.html', context)

get() returned more than one Sub_Topic -- it returned 3

I have started a project using Django. Where I used add multiple sub-topics under one main topic by taking staticid. When I am giving same staticid to multiple sub-topics, I am getting the error below (get() returned more than one Sub_Topic -- it returned 3!).
Model:
class Sub_Topic(models.Model):
IMPORTANCE_SCORE = (
('LOW','Low'),
('NORMAL', 'Normal'),
('HIGH','High'),
)
staticid = models.ForeignKey(SID,on_delete=models.CASCADE, blank=True, default=None, null=True)
sub_topic = models.CharField(max_length=250)
Num_Of_Sub_subTopics = models.PositiveIntegerField(default=0)
Num_Of_Questions = models.PositiveIntegerField(default=0)
importance = models.CharField(max_length=6, choices= IMPORTANCE_SCORE, default='LOW')
complexity = models.PositiveIntegerField(default=0)
prerequisite = models.CharField(max_length=250)
def __str__(self):
return self.sub_topic
View:
class Sub_TopicDetailView(generics.RetrieveUpdateDestroyAPIView):
"""
GET sub_topic/:id/
PUT sub_topic/:id/
DELETE sub_topic/:id/
"""
queryset = Sub_Topic.objects.all()
serializer_class = Sub_TopicSerializer
def get(self, request, *args, **kwargs):
try:
a_sub_topic = self.queryset.get(staticid=kwargs["staticid"])
return Response(Sub_TopicSerializer(a_sub_topic).data)
except Sub_Topic.DoesNotExist:
return Response(
data={
"message": "Sub_Topic with id: {} does not exist".format(kwargs["staticid"])
},
status=status.HTTP_404_NOT_FOUND
)
#validate_request_data
def put(self, request, *args, **kwargs):
try:
a_sub_topic = self.queryset.get(staticid=kwargs["staticid"])
serializer = Sub_TopicSerializer()
updated_sub_topic = serializer.update(a_sub_topic, request.data)
return Response(Sub_TopicSerializer(updated_sub_topic).data)
except Sub_Topic.DoesNotExist:
return Response(
data={
"message": "Sub_Topic with id: {} does not exist".format(kwargs["staticid"])
},
status=status.HTTP_404_NOT_FOUND
)
Error:
get() returned more than one Sub_Topic -- it returned 3!
How do I overcome this?
If you have a main topic (say, "donuts"), and many subtopics within that ("plain donuts", "chocolate donuts", "vanilla donuts", ...), you cannot reference a subtopic by just saying "donuts", you have to be more specific.
Your sub-topic views should accept a sub-topic ID, not the main topic ID. Try changing this:
a_sub_topic = self.queryset.get(staticid=kwargs["staticid"])
# 'staticid' is the foreign key of the main topic: it is
# the same for many sub-topics!
to this:
a_sub_topic = self.queryset.get(id=kwargs["id"])
# 'id' is the primary key field generated automatically by Django:
# it's unique for every sub-topic
If instead you want to display all sub-topics for a given topic, then you should use filter() instead of get():
sub_topics = self.queryset.filter(staticid=kwargs["staticid"])

Resources