-
-
Notifications
You must be signed in to change notification settings - Fork 151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixes #96: Improve string representation of sortedm2m relationships #101
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @rohithasrk. great work!
I left you comments on parts that need improvement.
TODO
In order to improve the pull request so it can be merge you should:
- address the inline comments
- add at least an assertion in the test suite that checks the
m2mprint
model__str__
method - squash your commits into one
- add a reference to this new feature in the README (a few lines of text are enough, don't worry about the english grammar/style, I will help you, just show that you are willing to put effort into this)
Automated tests
Regarding the tests, I suggest to add an assertion here:
https://github.com/gregmuellegger/django-sortedm2m/blob/master/sortedm2m_tests/test_field.py
This means you should probably move your new model to this file:
https://github.com/gregmuellegger/django-sortedm2m/blob/master/sortedm2m_tests/models.py
example/testapp/models.py
Outdated
@@ -9,10 +9,13 @@ class Car(models.Model): | |||
def __unicode__(self): | |||
return self.plate | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
follow pep8 conventions and add an additional blank line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall I use tools like autopep8 to correct all pep8 errors?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. Thanks :)
example/testapp/models.py
Outdated
@@ -9,10 +9,13 @@ class Car(models.Model): | |||
def __unicode__(self): | |||
return self.plate | |||
|
|||
class m2mprint: | |||
def __unicode__(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
__unicode__
is deprecated (in python 3 there's only unicode strings). Use __str__
with django.utils.encoding.python_2_unicode_compatible.
Subclass the django base model class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
__unicode__
method is being used almost everywhere in this package. Shall I replace all the occurrences?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rohithasrk I'm looking now.. wait a minute and I'll come back to you regarding this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest you to move this new model to:
https://github.com/gregmuellegger/django-sortedm2m/blob/master/sortedm2m_tests/models.py
It will be easier for you to import that model in the test suite.
In the test suite you will have to ensure the representation of the model is what you expect and I'm pretty confident declaring __unicode__
only would not work on python3.
I leave the solution up to you, as long as the test works, I'm fine with it.
I pointed out that solution because in the OpenWISP modules I try to use python_2_unicode_compatible
as much as possible but this package was probably developed before this utility was added to django.
sortedm2m/fields.py
Outdated
@@ -212,17 +212,23 @@ class SortedManyToManyField(_ManyToManyField): | |||
''' | |||
Providing a many to many relation that remembers the order of related | |||
objects. | |||
|
|||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are adding spaces and changing a line that doesn't need to be changed, try to revert this change (insert a blank line without space, commit and then squash/fixup).
sortedm2m/fields.py
Outdated
if self.base_class: | ||
return type(str(name), (models.Model, self.base_class), attrs) | ||
else: | ||
return type(str(name), (models.Model,), attrs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this whole if block can be improved. You are repeating str(name)
, (models.Model,)
and attrs
twice.
You could store that tuple in an auxiliary variable and insert the base class if needed.
Could you try this? You could also try a different idea if you like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Seems good.
sortedm2m/fields.py
Outdated
|
||
def create_intermediate_model(self, klass): | ||
# Construct and return the new class. | ||
from_field_name, from_field = self.get_intermediate_model_from_field(klass) | ||
to_field_name, to_field = self.get_intermediate_model_to_field(klass) | ||
sort_value_field_name, sort_value_field = self.get_intermediate_model_sort_value_field(klass) | ||
|
||
meta = self.get_intermediate_model_meta_class( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
more readable would be:
meta = self.get_intermediate_model_meta_class(klass,
from_field_name,
to_field_name,
sort_value_field_name)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I agree
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using tools like autopep8 should help doing all these. Anyways, will fix them soon.
sortedm2m/fields.py
Outdated
meta = self.get_intermediate_model_meta_class( | ||
klass, from_field_name, to_field_name, sort_value_field_name) | ||
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are adding spaces and changing a line that doesn't need to be changed, try to revert this change (insert a blank line without space, commit and then squash/fixup).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad. Will fix them
Thanks for the review @nemesisdesign. I'll update the PR soon |
Regarding the tests, I suggest to add an assertion here: https://github.com/gregmuellegger/django-sortedm2m/blob/master/sortedm2m_tests/test_field.py This means you should probably move your new model to this file: https://github.com/gregmuellegger/django-sortedm2m/blob/master/sortedm2m_tests/models.py I will copy this text in the review text. Federico |
aedd21a
to
fc511ab
Compare
@nemesisdesign : I've updated the PR by squashing the commits and modifying the commits with the changes you've suggested. Please review. Thanks |
@rohithasrk great! There's only one thing missing, an assertion in the test suite. Did you get one of my last replies? I see that you have created a model in the testapp but that is not enough. We should ensure that the test model is behaving as expected. |
@gregmuellegger I think this PR is going to be ready soon |
@nemesisdesign : Added a test case too. I'm kinda new to writing tests, so please review. Thank you |
This test seems to be giving wrong results. I'll update the revised version in a while |
@nemesisdesign : I think this is fine. I've written a test comparing the |
@nemesisdesign @gregmuellegger I'm curious. Can this be merged? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rohithasrk I can see you don't have a lot of experience with unit testing in general. No worries, I left you some comments that give you precise instructions on what to do. Please read carefully my comment and try to understand why I left them, if you have questions, please don't hesitate.
Implement these changes and you are done with this.
I'll ping @gregmuellegger to merge this as soon as he can.
sortedm2m_tests/models.py
Outdated
class m2mprint: | ||
|
||
def __unicode__(self): | ||
return unicode(self.book) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please do something like the following:
class BaseCarThrough(object):
def __str__(self):
return "Relationship to {0}".format(self.book.name)
def __unicode__(self):
return u"{0}".format(str(self))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This throws and error saying the maximum recursion depth has exceeded. Which IMO, is because str(self)
is not invoking the __str__
method. It is calling the __unicode__
method. @nemesisdesign
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, it's better to write them independently IMO. What say @nemesisdesign?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes whatever it works as long as there is an assertion in the test that checks for the result is what we expect and the assertion works with python2 and python3.
sortedm2m_tests/models.py
Outdated
class Shelf(models.Model): | ||
books = SortedManyToManyField('Book', related_name='shelves') | ||
books = SortedManyToManyField('Book', related_name='shelves', base_class=m2mprint) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change m2mprint
to BaseBookThrough
sortedm2m_tests/test_field.py
Outdated
shelf_book = shelf.books.through | ||
|
||
if hasattr(shelf_book, '__unicode__'): | ||
self.assertEqual(shelf_book.__unicode__, m2mprint.__unicode__) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rohithasrk, this is not good enough, you should do something like:
self.assertEqual(str(shelf), ''Relationship to {0}".format(shelf.name)
get rid of shelf_book = shelf.books.through
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't it be shelf.book.name? As shelf doesn't have an attribute name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rohithasrk Yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nemesisdesign : shelf.book.name
throws errors too, as shelf has no attribute book. That is why I've used shelf_book = shelf.books.through
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
apply the suggestions I suggested to you on IRC.
Do something like:
relationship = shelf_book.objects.first()
self.assertEqual(str(relationship), "<expected representation of the relationship here>")
sortedm2m_tests/test_field.py
Outdated
if hasattr(shelf_book, '__unicode__'): | ||
self.assertEqual(shelf_book.__unicode__, m2mprint.__unicode__) | ||
else: | ||
self.assertTrue(True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Get rid of the if/else condition please.
BTW: adding stuff like self.assertTrue(True)
is really bad. Don't do it!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nemesisdesign, I added these if/else conditions just to check if the base_class parameter has been provided. Tests fail when base_class parameter isn't provided. It shouldn't be the case right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have not understood your last comment very well.
You have to write a test which checks the output of the __str__
method is what you expect. No need to add if/else blocks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nemesisdesign : I mean, should the test fail if a sortedm2m relationship doesn't have a base_class parameter? In my opinion, No.
As in the above mentioned changes, the test is failing. And that is why, I've used if/else conditions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why should the test fail if you defined it in the model? Am I missing something?
example/testapp/models.py
Outdated
@@ -10,9 +10,15 @@ def __unicode__(self): | |||
return self.plate | |||
|
|||
|
|||
class m2mprint: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
apply the same suggestions I gave you in https://github.com/gregmuellegger/django-sortedm2m/pull/101/files#r108227092
sortedm2m_tests/test_field.py
Outdated
shelf_book = shelf.books.through | ||
|
||
if hasattr(shelf_book, '__unicode__'): | ||
self.assertEqual(shelf_book.__unicode__, m2mprint.__unicode__) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
apply the suggestions I suggested to you on IRC.
Do something like:
relationship = shelf_book.objects.first()
self.assertEqual(str(relationship), "<expected representation of the relationship here>")
sortedm2m_tests/test_field.py
Outdated
if hasattr(shelf_book, '__unicode__'): | ||
self.assertEqual(shelf_book.__unicode__, m2mprint.__unicode__) | ||
else: | ||
self.assertTrue(True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why should the test fail if you defined it in the model? Am I missing something?
sortedm2m_tests/models.py
Outdated
class m2mprint: | ||
|
||
def __unicode__(self): | ||
return unicode(self.book) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes whatever it works as long as there is an assertion in the test that checks for the result is what we expect and the assertion works with python2 and python3.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is the last round of changes!
sortedm2m_tests/test_field.py
Outdated
def test_base_class_str(self): | ||
shelf = self.model.objects.create() | ||
shelf.books.add(self.books[0]) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove blank line
sortedm2m_tests/test_field.py
Outdated
|
||
through_model = shelf.books.through | ||
instance = through_model.objects.first() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove blank line
example/testapp/models.py
Outdated
@@ -10,9 +10,15 @@ def __unicode__(self): | |||
return self.plate | |||
|
|||
|
|||
class BaseCarThrough(object): | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove blank line
sortedm2m_tests/test_field.py
Outdated
shelf.books.add(self.books[0]) | ||
|
||
through_model = shelf.books.through | ||
instance = through_model.objects.first() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unfortunately I forgot first()
is not supported on old django versions.
Try rewriting this line as:
instance = through_model.objects.all()[0]
81adf42
to
3279680
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gregmuellegger I think it's ready to be merged.
@gregmuellegger Resolved the conflicts and pushed. I think this can be merged. |
I'm deeply sorry for this contribution to being so long on the waiting! I was deeply dived into personal projects (like getting maried 💒) which was that I spent nearly no time in front of a computer. Thank you for your patience and for the contribution and the amazing guidance that you @nemesisdesign gave here! I will release this feature in a new release in the next days. |
It is released! You can find your contribution as part of: https://pypi.python.org/pypi/django-sortedm2m |
Thank you @gregmuellegger |
Thanks for merging and congratulations for your marriage! |
Fixes #96
This PR aims to improve the string representation of SortedM2M relationship when deleting one from an admin panel. I basically added a
base_class
parameter, a class which is inherited by the through-model of a sortedm2m relationship. This is basically so that the users of django-sortedm2m can add their own__unicode__
method and improve the string representation.For ex., in
example/testapp/models.py
we've passed a base_class argument when a relationship is being established. Check the image below,@gregmuellegger : Please review.