.filter() versus .first() in django - django-models

>>> user = User.objects.filter(username='xyz')
>>> user
<QuerySet [<User: xyz>]>
>>> user.id
Gives error -> Traceback (most recent call last):
File "", line 1, in
AttributeError: 'QuerySet' object has no attribute 'id' in Django
But after applying .first() I get the correct user id
>>> user = User.objects.filter(username='xyz').first()
>>> user
<User: xyz>
>>> user.id
1
>>>
Why is it so ?

user = User.objects.filter(username='xyz')
Using only filter it will provide you all the users whose username='xyz'.
It can be many.
BUT
If you use user = User.objects.filter(username='xyz').first()
It will provide only the first object not all. It will return only one object of user whose username='xyz'.

Related

AttributeError: 'NoneType' object has no attribute '<attribute-name>' , while testing Django UserModel

I am experiencing a very annoying problem, I would appreciate your help.
I will explain the problem:
I have a user-model that I run unit tests on.
The tests fail time and time again because of the problem that it does not recognize the attributes at all.
I would very much appreciate a solution if anyone has one.
Attaching the relevant code.
the model:
from django.db import models
from django.contrib.auth.models import (
AbstractBaseUser,
BaseUserManager,
PermissionsMixin
)
class UserManager(BaseUserManager):
"""manager for users"""
def create_user(self, email, password=None, **extra_fields):
"""create save and return a new user"""
user = self.model(email=self.normalize_email(email), **extra_fields)
user.set_password(password)
user.save(using=self._db)
class User(AbstractBaseUser, PermissionsMixin):
"""user in the system"""
email = models.EmailField(max_length=255, unique=True)
name = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
the tests:
from django.test import TestCase
from django.contrib.auth import get_user_model
class ModelTests(TestCase):
def test_create_user_with_email_successful(self):
email = 'test#example.com'
password = 'testpass123'
user = get_user_model().objects.create_user(
email=email,
password=password,
)
self.assertTrue(user.check_password(password))
self.assertEqual(user.email, email)
def test_new_user_email_normelaized(self):
sample_emails = [
['test1#EXAMPLE.com', 'test1#example.com'],
['Test2#example.com', 'test2#example.com'],
['TEST3#EXAMPLE.COM', 'test3#example.com'],
['test4#example.COM', 'test4#example.com'],
]
for email, expected in sample_emails:
user = get_user_model().objects.create_user(email, 'sample123')
self.assertEqual(user.email, expected)
the problem:
ERROR: test_create_user_with_email_successful (core.tests.test_models.ModelTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/app/core/tests/test_models.py", line 16, in test_create_user_with_email_successful
self.assertTrue(user.check_password(password))
AttributeError: 'NoneType' object has no attribute 'check_password'
======================================================================
ERROR: test_new_user_email_normelaized (core.tests.test_models.ModelTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/app/core/tests/test_models.py", line 29, in test_new_user_email_normelaized
self.assertEqual(user.email, expected)
AttributeError: 'NoneType' object has no attribute 'email'
----------------------------------------------------------------------
Ran 6 tests in 0.313s
FAILED (errors=2)
Your UserManager is not returning anything that's why you are getting None object when you are creating user.
You can use replace the below code with your UserManager and that will fix all your test cases.
class UserManager(BaseUserManager):
"""manager for users"""
def create_user(self, email, password=None, **extra_fields):
"""create save and return a new user"""
user = self.model(email=self.normalize_email(email.lower()), **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
Here I am doing email.lower() because normalize_email function only normalizes string before # that means it returns Test2#example.com as it is instead
of test2#example.com and TEST3#EXAMPLE.COM as TEST3#example.com which is not what you are expecting here.
Ok... so after a long time of trying to find the problem, it turns out it's really simple.
The problem was that no value was returned from the instance of the UserManger, so the object returned NoneType. And of course it's attributes were not identified.
I added a return user at the end of class and everything got identified back again.

What is the difference between ChildPage.objects.child_of(self) and ParentPage.get_children()?

I think that ChildPage.objects.child_of(self) and ParentPage.get_children() produce same result because subpage_types of ParentPage is just one ['ChildPage'].
But when I try to filter the result of ParentPage.get_children() there is an error.
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
child = self.get_children().live().public() # <- don't works
child = ChildPage.objects.child_of(self).live().public() # <- works
if request.GET.get('tag', None):
tags = request.GET.get('tag')
child = child.filter(tags__slug__in=[tags]) # <- error here
context["child"] = child
return context
Traceback (most recent call last):
Cannot resolve keyword 'tags' into field. Choices are: alias_of, alias_of_id, aliases, blogindexpage, blogpage, content_type, content_type_id, depth, draft_title, expire_at, expired, first_published_at, formsubmission, go_live_at, group_permissions, has_unpublished_changes, homepage, id, last_published_at, latest_revision_created_at, live, live_revision, live_revision_id, locale, locale_id, locked, locked_at, locked_by, locked_by_id, numchild, owner, owner_id, partnerindexpage, partnerpage, path, redirect, revisions, search_description, seo_title, show_in_menus, sites_rooted_here, slug, title, translation_key, url_path, view_restrictions, workflow_states, workflowpage
With self.get_children(), the page type of the child pages is not known in advance - a page's children may include multiple different types. Since Django querysets don't (as standard*) support combining data from multiple models, the results are returned as the basic Page type, which just contains the core fields common to all pages such as the title and slug. Filtering on tags therefore fails, because that field does not exist on the Page model.
With ChildPage.objects.child_of(self), Django knows in advance that the page type is ChildPage - if self had any child pages of other types - they would not be included in the results - so it can query directly on the ChildPage table, and consequently all fields of ChildPage (including tags) are available for filtering.
* Wagtail does provide a specific() method on the queryset to pull in the full data of the pages, but this is implemented as a postprocessing step after the main database query is done, so this still won't allow you to filter on fields that aren't part of the base Page model.

Mongoid validation validates_inclusion_of is not giving me an error

I have a mongodb document, and I am trying to create a validation
class Calculator
include Mongoid::Document
field :a, type: String
field :b, type: String
field :operator, type: String
validates_inclusion_of :operator, in: %w[sum difference multiplication division]
Then when I create a document in the console with an invalid operator field it doesn't give me an error.
c = Calculator.new(a: 3, b: 4, operator: 'fdad')
Validations are performed on persistence, e.g. when you call save or save!. They are not performed when you instantiate models (new call).

Wagtail ModelAdmin > How to use custom validation?

I am using wagtail ModelAdmin for some of my non page models and want to add some custom validation.
This is some of the code.
class EditPlanningView(EditView):
def publish_url(self):
return self.url_helper.get_action_url('publish', self.pk_quoted)
def unpublish_url(self):
return self.url_helper.get_action_url('unpublish', self.pk_quoted)
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
instance = form.save(commit=False)
if bool(request.POST.get('action-publish')):
try:
instance.publish(commit=True)
except PublishWithoutMeetingError as e:
form.add_error(
'planning_meeting',
e
)
return self.form_invalid(form)
When validation fails the invalid form is returned, but the error I added is not bound to the field. In stead a 'general error message' appears at the top.
Can someone help me out?
Cheers,
Robert
I think the error is in the following lines.
form.add_error(
'planning_meeting',
e
)
Actually can't say anything without knowing about PublishWithoutMeetingError, the type of e. Better to replace e with a string. And make sure the post method is not throwing any exceptions. Other than that, what you have done is correct. Read the following to also to check if you have missed any point.
Long Answer
There are two ways that you can achieve showing an error messages in forms.
Overriding the Form
Overriding the EditView
In both of these cases, you are going to use a method called add_error. That method takes 2 argument, field and error. From these two, error is the most important argument. The field simply state the field of the form that this error applies to. This can be None.
The error argument can be multiple types.
The error argument can be an instance of str. Then wagtail will assign the given error to the given field.
The error argument can be an instance of list of str. Then wagtail will assign the given list of errors to the given field.
The error argument can be an instance of dict with str keys and str or list of str values. In this case field should be None. The keys will be used as the fields for the errors given by values.
The error argument can be an instance of ValidationError exception. You can create a ValidationError using a str, list, or dict, which represent the above three cases.
Overriding the Form
In the form clean method need to be overridden in order to find errors.
from wagtail.admin.forms.models import WagtailAdminModelForm
class ExtraForm(WagtailAdminModelForm):
def clean(self):
cleaned_data = super().clean() # Get the already cleaned data. Same as self.cleaned_data in this case. But this way is better.
title = cleaned_data.get('title') # Get the cleaned title
if title is None: # Title is never None here, but still..
return cleaned_data
title = title.strip() # Do some formatting if needed
if title.startswith('A'): # Validation
self.add_error('title', 'Title cannot start with A') # Validation error
else:
cleaned_data['title'] = title # Use the formatted title
return cleaned_data
class MyModel(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=500, default='', blank=False)
# Or any other fields you have
base_form_class = ExtraForm # Tell wagtail to use ExtraForm instead of the default one
Overriding the EditView
This way is same as the way that you have mentioned in the question. You need to override post method. You need to check if the form associated with the EditView is valid or invalid and return the appropriate form.
To check validity, is_valid method of the form is used by default. That method will clean the form and check if there are errors added to the form.
If form is valid, you need to return self.valid_form and self.invalid_form otherwise.
Unlike overriding the Form, you can access the request here.
class MyEditView(EditView):
def post(self, request, *args, **kwargs):
form = self.get_form() # Get the form associated with this edit view
if form.is_valid(): # Check if the form pass the default checks
my_field = request.POST.get('my_field') # You can access the request
title = form.cleaned_data.get('title') # You can access the form data
if title != my_field: # Validation
form.add_error('title', 'Title must match my_field') # Validation error
return self.form_invalid(form) # Return invalid form if there are validation errors
return self.form_valid(form) # Return the valid form if there are no validation errors
else:
return self.form_invalid(form) # Return invalid form if default check failed
class MyModelAdmin(ModelAdmin):
model = MyModel
menu_label = 'My Model'
list_display = ('id', 'title')
search_fields = (
'title',
)
edit_view_class = MyEditView # Tell wagtail to use MyEditView instead of the default one.

Django DatabaseError "more than one row returned by a subquery used as an expression" Editable related fields to object

I am trying to add inlines to my template but continue to get a Database error:
more than one row returned by a subquery used as an expression
I have 3 objects in my models.py that relate to each other. The user will be able to see which Teacher is selected and have all Owners under that Teacher listed (Teacher and Owner will only appear as an uneditable list). I'd like to have all the Pets under the Owner listed and editable. Any ideas on why I am receiving this error? And how I may be able to accomplish my goal?
models.py
class Teacher(models.Model):
teacher = models.CharField(max_length=300)
class Owner(models.Model):
relevantteacher = models.ForeignKey(Teacher)
owner = models.CharField(max_length=300)
class PetName(models.Model):
relevantowner = models.ForeignKey(Owner)
pet_name = models.CharField(max_length=50)
forms.py
class OwnerForm(forms.ModelForm):
class Meta:
model = Owner
PetNameFormSet = inlineformset_factory(Owner,
PetName,
can_delete=False,
extra=3,
form=OwnerForm)
views.py
def petname(request, teacher_id):
teacher = get_object_or_404(Teacher, pk=teacher_id)
owners = Owner.objects.filter(relevantteacher=teacher_id)
if request.method == "POST":
petNameInlineFormSet = PetNameFormSet(request.POST, request.FILES, instance=owners)
if petNameInlineFormSet.is_valid():
petNameInlineFormSet.save()
return HttpResponseRedirect(reverse('success'))
else:
petNameInlineFormSet = PetNameFormSet(instance=owners) //error might be here?
context = {'teacher': teacher, 'owners': owners, 'petNameInlineFormSet' : petNameInlineFormSet}
return render(request, 'petname.html', context)
Update:
Here is the traceback:
File "hde/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
111. response = callback(request, *callback_args, **callback_kwargs)
File "/views.py" in petname
60. petNameInlineFormSet = PetNameFormSet(instance=owners)
File "lib/python2.7/site-packages/django/forms/models.py" in __init__
697. queryset=qs, **kwargs)
File "lib/python2.7/site-packages/django/forms/models.py" in __init__
424. super(BaseModelFormSet, self).__init__(**defaults)
Needed to pass only 1 object to the instance
owner = owners[0]
then
instance=owner
However, I am only able to add/edit pet names 1 owner at a time.
Thanks aamir for the help!
I believe your error is in the second line of the views.py file. I believe it is the call to the get_object_or_404 method causing the error when you try to specify teacher.id in your template. The call to the get_object_or_404 method is returning more than one row from the database, so calling teacher.id is not possible on it from more than one row.

Resources