looking at this example
#dataclass
class Something:
val1: int = 1
val2: int
VSCode with Pylance shows the error "Attributes without a default cannot follow attributes with one" in the editor. This is all good.
I have my own decorator that should enforce different constraints.
#model
class Something:
key1: Key[int]
# this violates a custom constraint on having exactly one attribute of type `Key`
key2: Key[int]
I have implemented model such that it throws an exception since there are two keys. This works too.
Now, the problem is, that the user doesn't see this error in the editor. Instead, the testing widget with pytest encounters discovery errors and the user needs to check the Python Output console to see what is going on.
How can I make violations to my custom constraints visible in the editor?
Related
I am currently working on a reference documentation that was initially documented manually with .. class:: All classes in the modules where documented this way.
However, it was necessary recently to allow some automation to the documentation, such as show-inheritance of each class, but the problem now is how to combine the current manual documentation with autodoc.
I have tried to just simply replace .. class:: with .. autoclass:: but this resulted in a output where the description and parameters descriptions that were manually typed into the documentation (as against being generated from docstrings) to not be rendered in the output. Can anyone advise on what to do to solve this?
So here is more details:
Take a look at this:
.. autoclass:: wagtail.blocks.CharBlock
:show-inheritance:
.. class:: CharBlock
A single-line text input. The following keyword arguments are accepted in addition to the standard ones:
:param required: If true (the default), the field cannot be left blank.
:param max_length: The maximum allowed length of the field.
:param min_length: The minimum allowed length of the field.
:param help_text: Help text to display alongside the field.
:param validators: A list of validation functions for the field (see `Django Validators <https://docs.djangoproject.com/en/stable/ref/validators/>`__).
:param form_classname: A value to add to the form field's ``class`` attribute when rendered on the page editing form.
The output:
enter image description here
The issue is I have to repeat the class name, which would be rather confusing for the user of the documentation. autoclass would not render the parameters, hence the need for class. I need autoclass to be able to show the inheritance of the class(Bases). So I don't know if there is a directive that can perform the functions of class without me having to mention the class name, or is there any way I can force class not to take in any arguments?
I hope this clearer. Thanks
It is possible to use autodoc for some things (in this case it's just :show-inheritance:) and "manual" documentation (content that does not come from a docstring) for others, within the same directive.
Remove .. class:: and ensure that all lines after the .. autoclass:: line have the same indentation. I think this should produce the output that you want.
.. autoclass:: wagtail.blocks.CharBlock
:show-inheritance:
A single-line text input. The following keyword arguments are accepted in addition to the standard ones:
:param required: If true (the default), the field cannot be left blank.
:param max_length: The maximum allowed length of the field.
:param min_length: The minimum allowed length of the field.
:param help_text: Help text to display alongside the field.
:param validators: A list of validation functions for the field (see `Django Validators <https://docs.djangoproject.com/en/stable/ref/validators/>`__).
:param form_classname: A value to add to the form field's ``class`` attribute when rendered on the page editing form.
I have the following model (for demonstration purposes):
class ProjectModule(models.Model):
name = models.CharField(...)
module = models.ForeignKey('ProjectModule', ....., on_delete=models.PROTECT)
... (many more fields and relations) ...
I wrote a function to merge instances to get rid of duplicates and such. For this I need to change all related objects of the first object (succumber) and point them to their new parent (a submodule of the survivor). After changing all foreign keys I delete the succumber.
new_parent = survivor.projectmodule_set.filter(name=entry.name).first()
succumber.projectmodule_set.update(module=new_parent)
succumber.delete()
But that's not working because Django throws the following error:
"Cannot delete some instances of model 'ProjectModule' because they are referenced through a protected foreign key: 'ProjectModule.module'", <QuerySet [<ProjectModule: 1.1 : LCP : 5583>, <ProjectModule: 1.2 : JB1 : 5583>, <ProjectModule: 1.3 : JB2 : 5583>]>
Now, I just changed these (reverse) related objects and gave them a new foreign key. Their name even reflects that (5583 is the id of the new parent). Reloading the succumber before deleting does not help either. It just can not "forget" these relation and therefore can not be deleted although the relations have already changed.
I also tried the "long approach":
for submodule in succumber.projectmodule_set.all():
submodule.module = new_parent
submodule.save()
But no luck. What am I doing wrong here?
Update
Sure, I can share a bit more code but I think this will only make things more confusing.
These maintenance views use a generic function I wrote that can merge model instances together. (I am omiting sanity checks and other unrelated stuff like standard processing of the function and unique field handling, etc)
def merge_model_instances(self, succumber, survivor, *, callback = None, debug = False, **kwargs):
After some error checks the function gets all relations and all their related objects:
foreign_key_reverse_relations = [field for field in succumber._meta.model._meta.get_fields(include_hidden=True) if
field.auto_created and not field.concrete]
for reverse_relation in foreign_key_reverse_relations:
reverse_entries_succumber = reverse_relation.related_model.objects.filter(
**{reverse_relation.field.name: succumber})
Of course not all relations can be solved automatically so the function allows for the attachment of a callback function in which we can process special cases:
# custom handling for this reverse relation if a callback function was attached
if reverse_entries_succumber and callback and callback is not None and callable(callback):
reverse_entries_succumber = callback(reverse_relation, reverse_entries_succumber, survivor)
In this case there is a callback function with special treatments for "ProjectModule" objects. As these are self referential (and therefore form a tree) we only need to change the immediate children of root nodes (root nodes have .module=None). The rest can stay relative to each other.
def merge_callback(reverse_relation, reverse_entries, survivor, *, debug, **kwargs):
(... other stuff ...)
if reverse_relation.name == "projectmodule":
for entry in reverse_entries:
# we are looking for root modules because all other project modules do not need to be "moved"
if entry.module is None:
# we get the new parent (how is not really important here)
new_module = survivor.projectmodule_set.filter(name=entry.name).first()
# set new_module as new parent
entry.projectmodule_set.update(module=new_module) # this should save these child modules
# entry.refresh_from_db() # this does not work either :/
entry.delete() # objects are still attached to entry.projectmodule_set even though they should not be anymore and the error is thrown here
return reverse_entries
Given a simple one to many relationship where one page can be linked to multiple errors:
class Page(Base):
...
errors = relationship('Error', back_populates='page')
class Error(Base):
...
page = relationship('Page', back_populates='errors')
I add error objects to a page object simple by
page.errors.append(error)
This works. However PyCharm warns about Error would have no reference "append".
My Question: Is this just PyCharm not understanding that Page.errors is of type list(?) and not Error or is is there something I should do better?
If someone stumbles upon the same problem: I did not notify any problems yet and so I guess it is simply pycharm not recognising the type correctly. You can simple help it by using type hints:
class Page(Base):
...
errors: list = relationship('Error', back_populates='page')
I use Google Style Python Docstrings like in this Example for my Django's project.
When i create a class and using an attributes notation in docstring, Pycharm always say - "Unresolved reference".
class Post(models.Model):
"""
Class for posts.
Attributes:
title(str): Post title.
"""
title = models.CharField(max_length=120)
I understand that PyCharm doesn't see self for title and def __init__() function and write this error, but in Django I've never seen using def __init__() for classes inherited from models.
What should I do? Is this my error or does PyCharm not see context in this case? Should I use def __init__() or something else or write docsting in another way?
PyCharm does not appear to have support for this currently. The active issue for this in JetBrains issue tracker is https://youtrack.jetbrains.com/issue/PY-16760, please consider upvoting it to get it fixed. The only workaround if you want to avoid seeing these "Unresolved reference" errors in your docstrings is to disable the inspection in Preferences > Editor > Inspections > Unresolved references.
Another option that I have tried in the past is removing the "Attributes:" header and writing my attribute documentation on the same indentation level as the rest of the docstring. This no longer gives you a warning, but you are no longer conforming 100% to the Google Docstring Style Guide.
I solved this issue by adding a '# noqa' after the 'Attributes:', because i did not want to disable the unresolved reference warning.
So this would be the final docstring:
"""
Class for posts.
Attributes: # noqa
title(str): Post title.
"""
The following works for me: In your docstrings, writing Attribute: instead of Attributes: makes the error disappear. That allows you to keep the indentation level
We found this while testing, one machine was setup with MyISAM as the default engine and one was set with InnoDB as the default engine. We have code similar to the following
class StudyManager(models.Manager):
def scored(self, school=None, student=None):
qset = self.objects.all()
if school:
qset = qset.filter(school=school)
if student:
qset = qset.filter(student=student)
return qset.order_by('something')
The problem code looked like this:
print Study.objects.scored(student).count()
which meant that the "student" was being treated as a school. This got thru testing in with MyISAM because student.id == school.id because MyISAM can't do a rollback and gets completely re-created each test (resetting the autoincrement id field). InnoDB caught these errors because rollback evidently does not reset the autoincrement fields.
Problem is, during testing, there could be many other errors that are going uncaught due to duck typing since all models have an id field. I'm worried about the id's on objects lining up (in production or in testing) and that causing problems/failing to find the bugs.
I could add asserts like so:
class StudyManager(models.Manager):
def scored(self, school=None, student=None):
qset = self.objects.all()
if school:
assert(isinstance(school, School))
qset = qset.filter(school=school)
if student:
assert(isinstance(student, Student))
qset = qset.filter(student=student)
return qset.order_by('something')
But this looks nasty, and is a lot of work (to go back and retrofit). It's also slower in debug mode.
I've thought about the idea that the id field for the models could be coerced into model_id (student_id for Student, school_id for School) so that schools would not have a student_id, this would only involve specifying the primary key field, but django has a shortcut for that in .pk so I'm guessing that might not help in all cases.
Is there a more elegant solution to catching this kind of bug? Being an old C++ hand, I kind of miss type safety.
This is an aspect of Python and has nothing to do with Django per se.
By defining default values for function parameters you do not eliminate the concept of positional arguments — you simply make it possible to not specify all parameters when invoking the function. #mVChr is correct in saying that you need to get in the habit of using the parameter name(s) when you call the routine, particularly when there is inherent ambiguity in just what it is being called with.
You might also consider having two separate routines whose names quiet clearly identify their expected parameter types.