Skip to content

Conversation

@danparizher
Copy link
Contributor

Summary

Fixes #20906

@github-actions
Copy link
Contributor

github-actions bot commented Oct 16, 2025

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

Comment on lines 63 to 87
match statement {
Stmt::Assign(ast::StmtAssign { value, .. }) => {
if let Some(field_name) = is_nullable_field(value, checker.semantic()) {
checker.report_diagnostic(
DjangoNullableModelStringField {
field_name: field_name.to_string(),
},
value.range(),
);
}
}
Stmt::AnnAssign(ast::StmtAnnAssign {
value: Some(value), ..
}) => {
if let Some(field_name) = is_nullable_field(value, checker.semantic()) {
checker.report_diagnostic(
DjangoNullableModelStringField {
field_name: field_name.to_string(),
},
value.range(),
);
}
}
_ => continue,
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should avoid duplicating that much code. All we need is to extract value. Something like this should work

Suggested change
match statement {
Stmt::Assign(ast::StmtAssign { value, .. }) => {
if let Some(field_name) = is_nullable_field(value, checker.semantic()) {
checker.report_diagnostic(
DjangoNullableModelStringField {
field_name: field_name.to_string(),
},
value.range(),
);
}
}
Stmt::AnnAssign(ast::StmtAnnAssign {
value: Some(value), ..
}) => {
if let Some(field_name) = is_nullable_field(value, checker.semantic()) {
checker.report_diagnostic(
DjangoNullableModelStringField {
field_name: field_name.to_string(),
},
value.range(),
);
}
}
_ => continue,
}
let value = match statement {
Stmt::Assign(ast::StmtAssign { value, .. }) => value,
Stmt::AnnAssign(ast::StmtAnnAssign {
value: Some(value), ..
}) => value,
_ => continue,
};
if let Some(field_name) = is_nullable_field(value, checker.semantic()) {
checker.report_diagnostic(
DjangoNullableModelStringField {
field_name: field_name.to_string(),
},
value.range(),
);
}

Comment on lines 51 to 84
class IncorrectModelWithAnnotations(models.Model):
charfield: models.CharField[str, str] = models.CharField(max_length=255, null=True)
textfield: models.TextField[str, str] = models.TextField(max_length=255, null=True)
slugfield: models.SlugField[str, str] = models.SlugField(max_length=255, null=True)
emailfield: models.EmailField[str, str] = models.EmailField(max_length=255, null=True)
filepathfield: models.FilePathField[str, str] = models.FilePathField(max_length=255, null=True)
urlfield: models.URLField[str, str] = models.URLField(max_length=255, null=True)


class IncorrectModelWithSimpleAnnotations(models.Model):
charfield: models.CharField = models.CharField(max_length=255, null=True)
textfield: models.TextField = models.TextField(max_length=255, null=True)
slugfield: models.SlugField = models.SlugField(max_length=255, null=True)
emailfield: models.EmailField = models.EmailField(max_length=255, null=True)
filepathfield: models.FilePathField = models.FilePathField(max_length=255, null=True)
urlfield: models.URLField = models.URLField(max_length=255, null=True)


class CorrectModelWithAnnotations(models.Model):
charfield: models.CharField[str, str] = models.CharField(max_length=255, null=False, blank=True)
textfield: models.TextField[str, str] = models.TextField(max_length=255, null=False, blank=True)
slugfield: models.SlugField[str, str] = models.SlugField(max_length=255, null=False, blank=True)
emailfield: models.EmailField[str, str] = models.EmailField(max_length=255, null=False, blank=True)
filepathfield: models.FilePathField[str, str] = models.FilePathField(max_length=255, null=False, blank=True)
urlfield: models.URLField[str, str] = models.URLField(max_length=255, null=False, blank=True)

charfieldu: models.CharField[str, str] = models.CharField(max_length=255, null=True, blank=True, unique=True)
textfieldu: models.TextField[str, str] = models.TextField(max_length=255, null=True, blank=True, unique=True)
slugfieldu: models.SlugField[str, str] = models.SlugField(max_length=255, null=True, blank=True, unique=True)
emailfieldu: models.EmailField[str, str] = models.EmailField(max_length=255, null=True, blank=True, unique=True)
filepathfieldu: models.FilePathField[str, str] = models.FilePathField(
max_length=255, null=True, blank=True, unique=True
)
urlfieldu: models.URLField[str, str] = models.URLField(max_length=255, null=True, blank=True, unique=True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need all those tests? They seem redundant to me, considering that we ignore the annotation.

@ntBre ntBre changed the title [flake8-django] Fix DJ001 not detecting null=True violations in annotated assignments (DJ001) [flake8-django] Apply DJ001 to annotated fields Oct 22, 2025
@ntBre ntBre added the bug Something isn't working label Oct 22, 2025
Copy link
Contributor

@ntBre ntBre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, if Micha's happy with it!

@MichaReiser MichaReiser merged commit 8a73519 into astral-sh:main Oct 27, 2025
38 checks passed
@danparizher danparizher deleted the fix-20906 branch October 27, 2025 14:23
dcreager added a commit that referenced this pull request Oct 27, 2025
* origin/main:
  Respect `--output-format` with `--watch` (#21097)
  [`pyflakes`] Revert to stable behavior if imports for module lie in alternate branches for `F401` (#20878)
  Fix finding keyword range for clause header after statement ending with semicolon (#21067)
  [ty] Fix bug where ty would think all types had an `__mro__` attribute (#20995)
  Restore `indent.py` (#21094)
  [`flake8-django`] Apply `DJ001` to annotated fields (#20907)
  Clearer error message when `line-length` goes beyond threshold (#21072)
  Update upload and download artifacts github actions (#21083)
  Update dependency mdformat-mkdocs to v4.4.2 (#21088)
  Update cargo-bins/cargo-binstall action to v1.15.9 (#21086)
  Update Rust crate clap to v4.5.50 (#21090)
  Update Rust crate get-size2 to v0.7.1 (#21091)
  Update Rust crate bstr to v1.12.1 (#21089)
  Add missing docstring sections to the numpy list (#20931)
  [`pydoclint`] Fix false positive on explicit exception re-raising (`DOC501`, `DOC502`) (#21011)
  [ty] Use constructor parameter types as type context (#21054)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DJ001 not raised when field has type annotation

3 participants