pylint-dev / pylint-django

Pylint plugin for improving code analysis for when using Django
Other
592 stars 117 forks source link

not recognizing one to one relation of models #188

Open vaibhavhrt opened 6 years ago

vaibhavhrt commented 6 years ago

WARNING: Please do not report issues about missing Django, see README!

TODO: make sure to post the output of pip freeze

>>> pip freezeargcomplete==1.8.2
astroid==2.0.1
autopep8==1.3.5
bbcode==1.0.31
beautifulsoup4==4.6.0
bs4==0.0.1
certifi==2018.4.16
cffi==1.11.5
chardet==3.0.4
colorama==0.3.9
Django==2.0.7
django-annoying==0.10.4
django-appconf==1.0.2
django-bootstrap-form==3.4
django-debug-toolbar==1.9.1
django-ipware==2.1.0
django-pure-pagination==0.3.0
django-ranged-response==0.2.0
django-registration-redux==2.4
django-simple-captcha==0.5.9
django-user-accounts==2.0.3
idna==2.7
isort==4.3.4
lazy-object-proxy==1.3.1
mccabe==0.6.1
mysqlclient==1.3.13
nltk==3.3
Pillow==5.2.0
pinax-theme-bootstrap==8.0.1
postmarkup==1.2.2
praw==5.4.0
prawcore==0.14.0
pybbm==0.19.0
pycodestyle==2.4.0
pycparser==2.18
pylint==2.1.1
pylint-django==2.0
pylint-plugin-utils==0.4
PyPDF2==1.26.0
pyperclip==1.6.2
pytz==2018.5
requests==2.19.1
selenium==3.13.0
six==1.11.0
sorl-thumbnail==12.4.1
sqlparse==0.2.4
Unidecode==1.0.22
update-checker==0.16
urllib3==1.23
wrapt==1.10.11

NOTES: make sure you have the latest version of 3rd party packages like rest_framework, factory, model_utils, etc. before reporting issues!

My Code screenshot 👇

image

My Code text 👇

class User(AbstractUser):
    """ Add more fields to default user model """
    name_full = models.CharField(max_length=255, blank=False, null=True)

    # Regex validator for phone number
    phone_regex = RegexValidator(
        # regex=r'^\+?1?\d{9,15}$',
        regex=r'^\d{9,15}$',
        message="Phone number must be entered in the format: '999999999'. Max 15 digits allowed."
    )
    phone_number = models.CharField(validators=[phone_regex], max_length=17, blank=False, null=True)

    def is_profile_complete(self):
        if self.userprofile.role == 2:
            return self.userprofile.is_userprofile_complete()
        else:
            return False

class UserProfile(models.Model):
    """ Database model for User's profile """
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    role = models.PositiveSmallIntegerField(default=0, blank=False, null=False)
    addr_l1 = models.CharField(max_length=127, blank=True, null=True)
    addr_l2 = models.CharField(max_length=127, blank=True, null=True)
    addr_pin_code = models.CharField(max_length=6, blank=True, null=True)

    # store friends
    friends = models.ManyToManyField("self")

    def __str__(self):
        return self.user.username

    def is_userprofile_complete(self):
        if self.addr_l1 and self.addr_l2 and self.addr_pin_code:
            return True
        return False

Pylint error 👇

{
    "resource": "/c:/Users/******/Documents/GitHub/********/user/models.py",
    "owner": "python",
    "code": "E1101",
    "severity": 8,
    "message": "E1101:Instance of 'User' has no 'userprofile' member",
    "source": "pylint",
    "startLineNumber": 25,
    "startColumn": 12,
    "endLineNumber": 25,
    "endColumn": 12
}

I think by looking at my code you can see what I am doing and it's working as expected. Its wip I will add more stuff to the method def is_profile_complete(self): later.
Since the models User and UserProfile have one to one relationship, I am accessing related objects using django's reverse relationship(or whatever it's called). But it pylint thinks it's an error as you can see.

godlark commented 5 years ago

The same for me, pylint-django==2.0.2

aiohttp==3.4.4
aioredis==1.2.0
amqp==2.3.2
apipkg==1.5
asn1crypto==0.24.0
astroid==2.0.4
async-timeout==3.0.1
asynctest==0.12.2
atomicwrites==1.2.1
attrs==18.2.0
backcall==0.1.0
beautifulsoup4==4.4.1
billiard==3.5.0.4
boto3==1.4.4
botocore==1.5.95
celery==4.1.1
certifi==2018.10.15
cffi==1.11.5
chardet==3.0.4
coverage==4.5.2
cryptography==1.9
cycler==0.9.0
decorator==4.0.6
diff-cover==1.0.5
dj-database-url==0.4.2
Django==2.0.6
django-countries==5.3.1
django-prometheus==1.0.15
django-waffle==0.14.0
django-webpack-loader==0.5.0
djangorestframework==3.8.2
docutils==0.14
execnet==1.5.0
flake8==3.6.0
flake8-debugger==3.1.0
flake8-isort==2.5
flake8-print==3.1.0
freezegun==0.3.11
google-api-python-client==1.5.2
hiredis==0.3.0
holidays==0.9.8
html5lib==0.999
httplib2==0.12.0
idna==2.7
idna-ssl==1.1.0
inflect==2.1.0
ipdb==0.11
ipython==6.4.0
ipython-genutils==0.2.0
isort==4.3.4
jedi==0.13.1
Jinja2==2.8
jinja2-pluralize==0.3.0
jmespath==0.9.3
kombu==4.2.0
lazy-object-proxy==1.3.1
lxml==3.5.0
MarkupSafe==1.1.0
matplotlib==1.5.1
mccabe==0.6.1
model-mommy==1.6.0
more-itertools==4.3.0
multidict==4.5.1
numexpr==2.4.3
numpy==1.13.0
oauth2client==3.0.0
pandas==0.19.2
paramiko==2.0.2
parso==0.3.1
pexpect==4.6.0
pickleshare==0.7.5
Pillow==3.1.2
pluggy==0.8.0
prometheus-client==0.4.2
prompt-toolkit==1.0.15
psycopg2==2.7.5
psycopg2-binary==2.7.5
ptyprocess==0.6.0
py==1.7.0
pyasn1==0.4.4
pyasn1-modules==0.2.2
pycodestyle==2.4.0
pycparser==2.19
pycurl==7.43.0
pyflakes==2.0.0
Pygments==2.2.0
pygobject==3.20.0
pylint==2.1.1
pylint-django==2.0.2
pylint-plugin-utils==0.4
pyparsing==2.0.3
pytest==3.7.0
pytest-asyncio==0.9.0
pytest-cache==1.0
pytest-cov==2.6.0
pytest-django==3.4.4
pytest-lazy-fixture==0.4.2
pytest-timeout==1.3.3
python-apt==1.1.0b1+ubuntu0.16.4.2
python-dateutil==2.6.0
python-redis-lock==3.2.0
pytz==2017.2
PyYAML==3.12
redis==2.10.6
requests==2.20.0
responses==0.10.4
rsa==4.0
s3transfer==0.1.13
scipy==0.17.0
simplegeneric==0.8.1
simplejson==3.16.0
six==1.10.0
ssh-import-id==5.5
tables==3.2.2
testfixtures==6.3.0
traitlets==4.3.2
typed-ast==1.1.0
unattended-upgrades==0.1
uritemplate==0.6
urllib3==1.24.1
vcrpy==2.0.1
vine==1.1.4
virtualenv==16.1.0
wcwidth==0.1.7
whitenoise==3.3.1
workdays==1.4
wrapt==1.10.11
yarl==1.2.6
atodorov commented 5 years ago

The problem here is that Django ORM will automatically create the User.userprofile attribute because the UserProfileClass has a 1-to-1 relationship back to User. This is really a problem with other such relationships, even ForeignKey (with or without a related_to attribute).

@carlio do you have any ideas that would work quick and easy ? Otherwise I will just have to experiment with augmentations or error suppression.

atodorov commented 5 years ago

@carlio I experimented with an idea that may work:

1) We need to parse the source tree before we can figure out which attributes should be added automatically. For that I use process_tokens(), possibly processmodule() because they are executed before the visit methods. Here I collect information about related attributes based on some heuristics (e.g. seen a ForeignKey field w/ or w/o related_name attribute).

2) My idea is to later monkey-patch TypeChecker to implement both Astroid and Token interfaces

3) Still not sure how to attach the collected information to the nodes pylint inspects.

Notes: this will work sometimes, but not always. Mainly it is limited to source files that have already been processed.

Another possibility is to use the astroid node transformations. ATM I don't know in what sequence they are executed and applied or if they can parse the entire source code before moving on to inspecting nodes.

Or maybe we try to load Django and bail out if it doesn't work ?

markbenepass commented 4 years ago

I'm having this issue as well on a one to one field. E1101: Instance of 'Model2' has no 'related_name' field.

they are defined on Model1 as field=models.OneToOneField("Model2", related_name="related_name" .... )

melikesofta commented 3 years ago

I am having the same issue, was this already resolved or am I missing something?

Django==3.1.3
pylint==2.6.0
pylint-celery==0.3
pylint-django==2.3.0
pylint-plugin-utils==0.6
atodorov commented 3 years ago

@markbenepass, @melikesofta have you tried the latest version of pylint-django? If the problem still persists open a new issue and include a reproducer.

markbenepass commented 3 years ago

Confirming problem still exists using pylint-django=2.4.2 with pylint=2.6.0. Currently updating pylint=2.7.4 to see if that fixes. Toy problem showing the setup, with two models, Car and Registration:

On Car:

registration = models.OneToOneField("Registration", related_name="car", on_delete=models.SET_NULL)

On Registration:

@property
def car_color(self):
   return self.car.color

This causes the following:

models.py:1428:19: E1101: Instance of 'Registration' has no 'car' member (no-member)

EDIT: confirmed this persists even with updated pylint=2.7.4

rconvent commented 1 year ago

I'm having the same issue, is it planned to be resolved ? Or is there a workaround?

Django==4.0.7 pylint==2.15.3 pylint-django==2.5.3

yanigisawa commented 2 months ago

@rconvent - I had the same issue in my VSCode editor until I explicitly configured the Pylint command with the django cli argument. I found it from this SO post.

pylint.args": ["load-plugins=pylint_django"]