Better Django code, automatically

Django Doctor fixes problems in your code, helping you build world-class websites, release faster, and reduce development costs. e.g.:

tasks.pyperformancemedium
def task(admin_blog_ids):
    queryset = CommentModel.objects.all()
-
    if len(queryset) > 100:
+
    if queryset.count() > 100:
        send_email_to_admin(queryset)

len(queryset) performs the count at application level. That is much less efficient than doing queryset.count(), which does the calculation at database level and just returns the count.

Read more
  • Department for International Trade
  • Giant
  • Motley fool
  • Lift Interactive
  • Lightmatter
  • Media Interactive
  • We Make Services
  • Sumo

Protect every code change automatically

Django Doctor reviews every new code change to prevent adding new vulernabilities, performance, or maintainability problems to your code. Enforce best practices without changing how your team works.

Ensure best practice

Automatically find and fix over 40 issues, so your team can focus on adding value. Reduce costs by preventing tech debt including hidden 404s, migrations blocking production rollback, hard-coded urls, and more.

Our maintainability checks

  • Importing models.py into migrations

    It's better to use apps.get_model, which guarantees the Model's fields will reflect the fields in the database even if models.py is vastly out of step with the database.

    Read more
  • Missing reverse migration

    It's good to, as a minimum, specify noop in RunPython so the migration can be skipped when going backwards, and even better to specify a function that undoes the migration.

    Read more
  • Hard-coded static asset URL in template

    Hard-coding static asset urls is brittle because the place the files are stored depends on the `STATICFILES_STORAGE` used - so if in prod the storage backend uploads to S3 or even renames the file then this hard-coded URL will break.

    Using "{% static ... %}" solves that as it knows exactly where the files are stored.

    Read more
  • Middleware order

    The order of middleware affections the outcome.

    Some middleware are dependant on the functionality of other middlware. For example a middleware that requires usage of request.session should come after the SessionMiddleware.

    Read more
  • URL name not unique

    URL names must be unique otherwise reverse('url_name') and {% url 'url_name' %} will link to the "wrong" page half of the time.

    Read more
  • Using reverse_lazy where reverse would be better

    Using reverse(...) is better than using reverse_lazy(...) when the url is being reversed after URLConf has been loaded.

    Read more
  • Middleware should be near the end

    Incorrectly ordered middleware can stop the middleware working as intended.

    Read more
  • Middleware should be near the top

    Incorrectly ordered middleware can stop the middleware working as intended.

    Read more
  • Hard-coded URL in template

    Hard-coding urls makes changing URLs harder, and makes linking to the wrong page easier - so harms maintainability.

    Read more
  • Brittle unique_for

    unique_for_date/month/year is very brittle and over time will likely be a source for unexpected behaviour.

    Read more
  • Importing setting file directly

    Using from django.conf import settings simplifies the code and makes it more maintainable.

    Read more
  • CharField with huge max_length

    Using TextField for very long string fields would simplify the code and make is easier to maintain.

    Read more
  • Deprecated NullBooleanField

    NullBooleanField is deprecated and will be removed a future version of Django.

    Read more
  • Redundant default arguments

    Stating defaults add complexity when reading the code but does not change Django's behaviour.

    Read more
  • Redundant setting

    Stating defaults add complexity when reading the code but does not change Django's behaviour.

    Read more
  • Field allows null but not blank

    If null is True and blank is False then validation becomes more complex.

    Read more
  • Non-unique primary_key

    A non-unique primary key allows the same value to be used for multiple records thus multiple results could be returned for Model.objects.get(pk=1).

    Read more
  • Relative path in TEMPLATES setting

    The TEMPLATE setting is a list of the template engines used when finding template files and rendering them.

    The DIRS key within the TEMPLATE list denotes the directories where the engine should look for template source files.

    DIRS must be absolute paths. Relative paths will not work.

    Read more
  • Back slashes in TEMPLATES settings

    The TEMPLATE setting is a list of the template engines used when finding template files and rendering them.

    The DIRS key within the TEMPLATE list denotes the directories where the engine should look for template source files.

    DIRS must be forward slashes, even in Windows. Forward slashes do not need escaping, and they are cross Operating System compatible.

    Read more
  • Nullable string field

    ORM queries and Python code are made more complex because values will sometimes be None and other times str.

    Read more
  • ForeignKey missing related_name

    ORM queries are more readable and relationships more explicit when related_name is specified.

    Read more
  • Model method order

    Increasing standardisation and predictability of your code style helps other developers read your code.

    Read more
  • Admin class not in admin.py

    Predictable project structure and following common patterns simplifies maintenance of a codebase.

    Django developers come to expect Admin-related objects to be in admin.py. Failure to do this will result in more time spent looking for where code lives.

    Read more
  • Tall Model

    A taller Model with many fields may be more difficult to maintain than a shorter Model with fewer fields.

    Read more
  • Huge models.py

    Taller models.py with many Models may be more difficult to maintain than shorter models.py with fewer Models.

    Read more
  • Tall models.py with a common prefix

    Taller models.py with many Models may be more difficult to maintain than shorter models.py with fewer Models.

    Read more
  • Enhance your security

    Protect your business and users against common vulnerabilities including missing Django fixes, XSS, Cross Site Request Forgery, clickjacking, session cookie hijacking, and more.

    Our security checks

  • Django version is no longer supported

    Django version is not receiving bug fixes, and security fixes, and data-loss fixes.

    Read more
  • Django bugfixes and additional features available

    New version of Django is available, including bug fixes and new features.

    Read more
  • Security middleware not activated

    Your website is vulnerable to a number of common hacker attacks because MIDDLEWARE setting is missing django.middleware.security.SecurityMiddleware.

    Read more
  • Clickjacking protection not activated

    Your website is vulnerable to clickjack attack because the MIDDLEWARE setting is missing django.middleware.clickjacking.XFrameOptionsMiddleware - so users can be tricked into interacting with your website without realising.

    Read more
  • Cross Site Request Forgery protection not activated

    Your website is vulnerable to CSRF attacks because the MIDDLEWARE setting is missing CsrfViewMiddleware - so a hacker can fool your website into thinking a request is coming from a logged in user.

    Read more
  • Cross Site Request Forgery protection weak to packet sniffing

    Your website is vulnerable because the CSRF_COOKIE_SECURE setting is not set - so hackers have an easier time stealing your CSRF cookies on HTTP connections, allowing them to circumvent your CSRF protection.

    Read more
  • HTTP Strict Transport Security protection not activated

    Your website is vulnerable to Man In The Middle attacks because the SECURE_HSTS_SECONDS setting is missing - so a hacker can intercept and change requests performed over HTTP.

    Read more
  • Subdomains not protected by HSTS

    Your website is vulnerable to Man In The Middle attacks on subdomains because the SECURE_HSTS_INCLUDE_SUBDOMAINS setting is missing - so a hacker can intercept and change requests performed over HTTP.

    Read more
  • HSTS browser preload list not activated

    Your website must set SECURE_HSTS_PRELOAD in order to be submitted to Chrome's list of sites that are hardcoded as being HTTPS only.

    Read more
  • Browser can be tricked into executing uploaded malicious code

    Your website is vulnerable to being tricked into executing uploaded malcious code because the SECURE_CONTENT_TYPE_NOSNIFF setting is not set.

    Read more
  • Website can be served with insecure HTTP

    Your website is vulnerable because the SECURE_SSL_REDIRECT setting is not set - so a hacker can read, intercept, and change requests performed over HTTP.

    Read more
  • Session cookie is vulnerable to packet sniffing attack

    Your website is vulnerable because the SESSION_COOKIE_SECURE setting is not set - so hackers have an easier time stealing your users' session cookies on HTTP connections.

    Read more
  • Session cookie is vulnerable to XSS attack

    Your website is vulnerable because the SESSION_COOKIE_HTTPONLY setting is not set - so hackers have an easier time stealing your users' session cookies using an XSS attack.

    Read more
  • Improve your website's speed

    Over half of Django websites are slowed by unoptimized code. Website speed has a direct impact on the success of your organization. Protect against unoptimized code that at best lose you customers and at worse bring your website down. Read more.

    Our performance checks

  • Using len(queryset) instead of queryset.count()

    len(queryset) performs the count at application level. That is much less efficient than doing queryset.count(), which does the calculation at database level and just returns the count.

    Read more
  • Not using foreign keys directly

    When working with foreign keys, accessing the related field will result in a database read. That can be eliminated by using *_id, which is the foreign key value that Django has already cached on the object to make this scenario more efficient.

    Read more
  • Comparing queryset.count() instead of checking queryset.exists()

    Comparing queryset.count() is less efficient than checking queryset.exists(), so use querySet.count() if you only want the count, and use queryset.exists() if you only want to find out if at least one result exists.

    Read more
  • Checking queryset truthiness instead of checking queryset.exists()

    This can load every row in the database table into memory because the queryset is evaluated. Checking if a queryset is truthy/falsey is much less efficient than checking queryset.exists().

    Read more
  • Zero developer overhead

    The checks happen automatically, and there is nothing to install or update. Cutting edge technology achieves near-zero noise for developers so your team can spend their time more productively. Make the most of our Django changelog comparison productivity tool.

    "Extremely positive. Django Doctor suggested useful changes, giving our senior developers time back."

    Jon Atkinson, Technical Director at Giant

    Our Price

    Individual

    Free

    forever

    Protects every PR: Yes

    Suggests the fix: Yes

    Codebase audit: Yes

    Public repositories: Unlimited

    Private repositories: No

    Support: No

    Install on GitHub
    Popular

    Organization

    $49

    per month

    Protects every PR: Yes

    Suggests the fix: Yes

    Codebase audit: Yes

    Public repositories: Unlimited

    Private repositories: 10

    Support: 24/7 email and chat

    Unlimited

    $99

    per month

    Protects every PR: Yes

    Suggests the fix: Yes

    Codebase audit: Yes

    Public repositories: Unlimited

    Private repositories: Unlimited

    Support: 24/7 email and chat