How can I override __str__ in models.py? - python

Apologies if this is a silly question, I am pretty new to python and django.
I am following along with a django tutorial, and we are creating a fake movie site that lists movies by genre, title, etc.
I am currently trying to override the __str__ function in the models.py file, so rather than displaying Genre (1), it displays the actual genre (ex. "Action").
Here's how my models.py looks currently:
from tkinter import CASCADE
from django.db import models
from django.utils import timezone
# Create your models here.
class Genre(models.Model):
name = models.CharField(max_length=255)
def __str__ (self):
return self.name
class Movie(models.Model):
title = models.CharField(max_length=255)
release_year = models.IntegerField()
number_in_stock = models.IntegerField()
daily_rate = models.FloatField()
genre = models.ForeignKey(Genre, on_delete=models.CASCADE)
date_created = models.DateTimeField(default=timezone.now)
However, vscode is underlining the
def __str__ (self):
When I hover over it, it tells me:
str does not return str pylint(E0307: invalid -str- returned
I tried looking at other solutions, but I could not find one that seemed to match my scenario, so I do apologize if this has been solved elsewhere, and I am too incompetent to understand the problem.
Thanks for your patience!

This question has a couple of good illustrations of why it's important to understand your code before you rely too heavily on IDEs and tools like pylint. I've two suggestions:
The pylint error is just a warning to indicate that your code might have a problem - it is not an absolute fact. The E0307 error says:
Used when a __str__ method returns something which is not a string.
It would be more accurate if it had said "when the pylint checker cannot be sure that you're returning a string". In this case, it's because it doesn't recognise that a Django CharField will in fact return a valid string instance. Your existing __str__ method would work perfectly fine if you ran the code - regardless of what pylint thinks.
You can work around this by forcing the return value to look like a string, e.g., with:
return str(self.name)
or
return f"{self.name}"
But it would be equally valid to disable the check for this line in pylint, with the understanding of why it reported the error. Just applying a fix found on Stack Overflow without understanding the issue is going to make it hard to debug your code in future.
There is a second, completely unrelated issue in your code which will cause you problems later, and that is this line:
from tkinter import CASCADE
I am pretty confident that this has been inserted by your IDE, without your explicitly adding it, because you tried to use CASCADE in your ForeignKey. This is the IDE trying to be helpful - unfortunately it has imported something completely useless, that can cause you problems when you try to deploy your code.
Both of these highlight an important principle: don't rely on IDEs or linters. They are not a substitute for understanding your own code.

Related

Python test coverage for class' __str__

I have a very basic questing regarding Python coverage tests using PyCharm IDE. In my Django models, all the __str__ methods are not covered in my tests.
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
What would be the appropriate way to test these?
This doesn't work, the test runs but the __str__ method is still not seen as covered.
class TestCategory(TestCase):
def test_category(self):
category = Category.objects.create(name='Test Category')
self.assertEqual(category.__str__(), 'Test Category')
The __str__() method is called whenever you call str() on an object.
You should try it using str() method on the instance object.
class TestCategory(TestCase):
def test_category(self):
category = Category.objects.create(name='Test Category')
self.assertEqual(str(category), 'Test Category')
Astik Anand has already provided a nice answer explaining how to technically force an execution of the __str__() method. I would, however, like to answer your question by interpreting it a bit differently - taking also the new information from the comments into account that the coverage tool seems to have an issue:
"How should I test the __str__() method although I know that my coverage tool is buggy and no matter what I have tried so far my tests will not get __str__() marked as covered by the tool?".
Take a step back. Forget about coverage. Coverage is not your goal. Your goal is to find all those bugs that might be found in __str__(). So, test __str__() as thoroughly as needed to reach this goal. This is how to test __str__() (mentally, not technically).
Sorry if you think that this was clear anyway - you would not believe what people do in the face of buggy coverage tools to reach their coverage goals...
I had the same issue. I solved the problem using the following information from
https://django-testing-docs.readthedocs.io/en/latest/coverage.html
pip install django-nose
in settings.py
add django_nose to INSTALLED_APPS
also, at the bottom of the file, add:
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
NOSE_ARGS = [
'--with-coverage',
'--cover-package=app_1,app_2',
'--cover-html' ]
I ran coverage HTML from the command line, and the report showed that the __str__ method from my model was covered.

Is it possible to do arithmetic operation on OuterRef expression?

I'm building an Experience booking system in django and I've the following models.
class Experience(models.Model):
name = models.CharField(max_length=20)
capacity = models.IntegerField()
class Booking(models.Model):
experience = models.ForeignKey(Experience)
occupied = models.IntegerField()
Each experience has a limited number of capacity and when user perform booking, it will be added to the Booking table with occupied number. Now how will I find the experiences which are not occupied completely?
available_experiences = Experience.objects.all().exclude(id__in=Subquery(Booking.objects.filter(occupied__gt=OuterRef('capacity') - request_persons).values_list('experience', flat=True)))
Here, request_persons is the number of required vacancy in an experience. This is not working and showing an error like 'ResolvedOuterRef' object has no attribute 'relabeled_clone'. Is it possible to do arithmetic operation on OutRef() expression like F()?
Without adding request_persons, the above code works. Why it is not possible to add a value to the OutRef() expression?
NOTE: My actual code is much complex one and it will be really great to get an answer without modifying the entire structure of the above code.
By doing arithmetic operations in the query referenced by OuterRef() directly you can resolve this issue:
available_experiences = Experience.objects.annotate(
total=models.F('capacity') - request_persons
).exclude(
id__in=Subquery(Booking.objects.filter(
occupied__gt=OuterRef('total')
).values_list('experience', flat=True))
)
If you found another way without modifying your structure or using RawSQL() or .extra(), let us know!
This seems to be fixed in Django 2.0: https://github.com/django/django/pull/9722/files
The fix can be backported to 1.11.x in a similar fashion:
from django.db.models.expressions import ResolvedOuterRef
if not hasattr(ResolvedOuterRef, 'relabeled_clone'):
ResolvedOuterRef.relabeled_clone = lambda self, relabels: self

Python constants

I am in the middle of a development in Django and we got some doubts about if we should use or not constants in the project. Exactly the situation is to use constants everywhere or not (we know constants in Django are not really read-only).
There are 2 scenarios and I would like to get your opinion about which one is better for you and why:
Scenario 1 (using constants)
constants.py
class CONST():
def NAME(): return "name"
def SURNAME(): return "surname"
def ZIPCODE(): return "zipcode"
def CITY(): return "city"
def CREATED(): return "created"
admin.py
from constants import CONST
class RegisterAdmin(admin.ModelAdmin):
list_display = (CONST.NAME(),CONST.SURNAME(),CONS.ZIPCODE())
list_filter = [CONST.ZIPCODE(),CONST.CITY()]
search_fields = [CONST.NAME(), CONST.SURNAME()]
date_hierarchy = CONST.CREATED()
models.py
from constants import CONST
class Register(models.Model):
name = models.CharField(CONST.NAME(), max_length=25)
surname = models.CharField(CONST.SURNAME(), max_length=25)
zipcode = models.IntegerField(CONST.ZIPCODE())
city = models.CharField(CONST.CITY(),max_length=20)
... and any view etc where you use text will be using contants ...
Scenario 2 (without constants)
admin.py
class RegisterAdmin(admin.ModelAdmin):
list_display = ("name","surname","zipcode")
list_filter = ["zipcode","city"]
search_fields = ["name","surname"]
models.py
class Register(models.Model):
name = models.CharField("name", max_length=25)
surname = models.CharField("surname", max_length=25)
zipcode = models.IntegerField("zipcode")
city = models.CharField("city",max_length=20)
I like the most the second scenario (I have been programming python from 2004), for me it looks more efficient, clear and easy to understand. The first scenario (proposed from Java/PHP programmers that now writes Python code) has the advantage that it helps the developer to detect that it made a mistake writing the "constant" so it is easier to detect errors and also it makes easier and quicker "massive changes" on this kind of texts without refactorizing the source code.
I would like to know which source code you would write or use and why.
Thank you,
Scenario 1 is awful. Unfortunately I know all too well the problems of working with Java/PHP developers who are learning python.
Perhaps you can compromise with those guys by proposing the use of python enums to address their concern. These are built-in in python 3.4+, and have been backported as far back as 2.4.
from enum import Enum
class Constant(Enum):
name = "name"
surname = "surname"
zipcode = "zipcode"
city = "city"
created = "created"
Now you can change the "values", say for example changing zipcode to be "potato" in the enum definition, whilst still using the name Constant.zipcode.value everywhere else in source code.
Scenario 1 doesn't make sense. You're using the constants to specify the verbose_name of the fields, and you're using them in the list_display option. The list_display option takes a list of fieldnames and NOT the verbose_name of the fields.
So you don't need constants at all for this. The field names are your "constants".
And IF you would need some "constants" in some case, you shouldn't use methods, just use properties, and just use normal class/attribute name conventions (so no uppercases):
class SomeConstants():
name = "name"
surname = "surname"
zipcode = "zipcode"
city = "city"
created = "created"
The Java/PHP guys might say something like "Yeah but what if one of the constants needs to be generated? Then you need a method!". Well, sure, so you make it a property:
class SomeConstants():
#property
def full_name(self):
return "{} {}".format(self.name, self.surname)
Because of the #property line above the method, it will execute and return the result of this method if you call SomeConstants().full_name.
Use of constants is sometimes good, but not here.
One, the first argument of a field is its verbose name, if it's missing then the name of the field is used by default. So using the same constant for its verbose name as for the field name is exactly the scenario in which the verbose name is completely unnecessary.
Just do
name = CharField(max_length=25)
or
name = CharField("A beautiful description for a name field", max_length=25)
but putting "name" there is completely redundant and thus wrong. Don't repeat yourself.
Second, your constant is actually used for two different things that you don't want to change at the same time: you use the same constant for the attribute name in admin.py, but for verbose name in models.py. They aren't going to stay the same thing.
Third, using constants is only really helpful if the value might change one day, or if it's not immediately obvious what it may mean, for e.g. PI=3.14.
But NAME = "name" does not make anything more obvious, and if it ever changes to NAME = "description" it immediately becomes actively misleading. You'd probably want to change the name of the constant in that case as well, won't you? Why did you ever use a constant then?
And it means that changing something in const.py changes the database structure that Django expects. Some day a developer is going to trip over that; keep field names in models.py.
Finally, at every place where you use the models, you need the actual field anyway. A Register instance is simply a class instance, after all, and you'll probably want to use fields like register.name anywhere. You don't put the names of attributes of other classes in constants, do you?
Just use the string.
What about something more straight-forward like:
constants.py
NAME = "name"
SURNAME = "surname"
admin.py
import constants
class RegisterAdmin(admin.ModelAdmin):
list_display = (constants.NAME, constants.SURNAME,constants.ZIPCODE)
This provides a unique place for the values in contrast to repeating them as in Scenario 2 and thus I would prefer it. It seems more clear than Scenario 1 to me. The "encapsulation" is performed by the module name, i.e. by using constants.xxx, so don't from constants import *.
The values here are obviously not constant, but neither are they in Scenario 1.
Everyone is in agreement that its a bad idea, but it could be that your Java friends are trying to implement the equivalent of a Java resource bundle, perhaps because they fondly remember how easy it is to work with .properties files.
This results in the use of keys in place of actual names and then later on the system will retrieve the correct resource from the bundle. This technique is used for translations and localization of applications.
A typical resource key looks like key.trx.hist.term.transfer.completed.success and at runtime the equivalent resource is rendered by the application.
If this is the case, in Python the approach is a bit different:
You start with a base language. This is what you write your code in.
All strings that need to be localized or translated are marked for translation, with the gettext library. For convenience this library provides a shortcut method _() which is used to mark strings for translation.
These strings are then extracted to a message file; which is forwarded to translators. The message file contains the file names where the string is used. Its a plain text file, much like the .properties file.
This message file is then compiled into an optimized format.
Based on the language/locale of the client, the correct translated strings are displayed.
You can read more on how this is implemented in django at the translation documentation.

Colander subclassing SchemaNode

I wonder if someone can help me. I'm explore deform and colander in a new project and was following the documentation about subclassing SchemaNode. However, whilst the documentation states that
subclass can define the following methods and attributes: preparer,
validator, default, missing, name, title, description, widget, and
after_bind.
when I define title, it doesn't seem to come through. Here is some example code that I'm using:
class LocationSchemaNode(colander.SchemaNode):
schema_type = colander.Int
title = 'Location'
missing = None
validator = colander.Range(
min=1,
min_err='Please select a valid location'
)
class PersonSchema(colander.Schema):
location_id = LocationSchemaNode()
However, when the form is rendered the label for the field is "Location Id" not "Location" as per the title defined in SchemaNode. If instead I write:
class PersonSchema(colander.Schema):
location_id = LocationSchemaNode(title="Location")
Then all appears as I want, but the documentation seems to state I don't need to do this, and if I do it kind of defeats the point of pre-defining a SchemaNode if I have to keep defining fields.
Am I missing something, or is deform doing something that it shouldn't be (I doubt that is going to be the case). Any help is much appreciated.
Keith
This appears to be a bug that was fixed: https://github.com/Pylons/colander/pull/183
Also, the patch seems to be in the latest available release of colander so an update to the latest version should fix this issue.
Correction:
The example given in that PR exactly matches this question, but the fix given didn't actually fix that exact issue! So, I filed another PR to fix that issue and used the example given in #183 as the test. You can manually patch your own copy if you can't wait for the fix to be introduced into the repo or the next release.

Why do I get an InvalidDocument exception when saving an object into MongoDB in Django for the first time?

I've been having a nightmare of a time trying to get MongoDB working with Django. I now have it successfully installed, but it errors upon the first attempt to save an object. I've been following this tutorial, and the Post model they present I have copied precisely.
Here is the code for the model:
from django.db import models
from djangotoolbox.fields import ListField
class Post(models.Model):
title = models.CharField(max_length=200)
text = models.TextField()
tags = ListField()
comments = ListField()
The post is actually created (and inserted) here:
post = Post.objects.create(
... title='Hello MongoDB!',
... text='Just wanted to drop a note from Django. Cya!',
... tags=['mongodb', 'django'],
... comments=['comment 1', 'comment 2']
... )
The full stack trace can be found here. The error itself is:
InvalidDocument: documents must have only string keys, key was <django.db.models.fields.CharField object at 0x22cae50>
Clearly MongoDB is functioning, because it wants the keys to be strings instead of integers. But why is the rum gone? Err, why are standard Django objects not able to save into the MongoDB database?
I have added the required CharField parameter max_length that was overlooked. It does not work in this case, nor does it if I also remove the lists.
I got this error until updating the django-mongodb-engine library to the current version in the 'develop' branch of the github repository. Simply using the instructions on the article shown will import the master branch.
I have also followed this tutorial and had same error. I managed to fix this problem.
What I did wrong is I used djangotoolbox from github (https://github.com/django-nonrel/djangotoolbox) repository, which was ver 0.9.2. Then I removed this and did it as tutorial states and used bitbucket (https://bitbucket.org/wkornewald/djangotoolbox) that is on ver 0.9.1 of djangotoolbox. Now it works fine.
Does this work:
post = Post(
... title='Hello MongoDB!',
... text='Just wanted to drop a note from Django. Cya!',
... tags=['mongodb', 'django'],
... comments=['comment 1', 'comment 2']
... )
Another thing to try:
class Post(models.Model):
title = models.CharField(primary_key=True, max_length=200)
I think by default Django uses auto-generated numeric values as primary keys. This would force your title to be your primary key... it's a char string, so it might solve the problem you're seeing. It's just a guess, because the actual error your seeing doesn't make too much sense to me.
From what I see, your model looks just fine. The error is funny; it is saying that the key to the attribute you are calling as "title" cannot be anything other than a string, but if I'm not mistaken, the string here is the key "title" and the value is the CharField() field, together representing a key/value pair in the Post document in your MongoDB. Try isolating the problem by jumping into the django shell:
python manage.py shell
and building your Post object from scratch. Use the model that dragonx has mentioned. Simple is best to debug these problems. If you are getting the same error when you try either to call Post.objects.create(p) for a Post object p, or when you call p.save(), try re-writing the Post model and give it another shot.
Finally, you can show us your settings.py file and see what your mongodb settings are there. If you say you are using django-nonrel, then there are basic checks you can make to ensure that python, django, and mongodb are talking to each other.
Let us know how you're doing.

Categories