Skip to content

implemented car listing api#14

Open
nomanakram199 wants to merge 13 commits into
developfrom
feat/car_listings
Open

implemented car listing api#14
nomanakram199 wants to merge 13 commits into
developfrom
feat/car_listings

Conversation

@nomanakram199

Copy link
Copy Markdown
Owner

No description provided.

Comment thread cars/models.py Outdated


class Car(TimeStampedModel):
model = models.ForeignKey(CarModel, on_delete=models.PROTECT, related_name='cars')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Can we separate out or make blocks of related fields for readability ?

Comment thread cars/models.py Outdated


class Car(TimeStampedModel):
model = models.ForeignKey(CarModel, on_delete=models.PROTECT, related_name='cars')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why we are using Protect here?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Because Protect prevents accidental data loss: you can’t delete a model while any Car references it. If we used CASCADE, deleting a model would delete all linked Car rows to null. And which will create the confusion regarding car model

Comment thread cars/models.py
class Car(TimeStampedModel):
model = models.ForeignKey(CarModel, on_delete=models.PROTECT, related_name='cars')
seller = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='cars')
year = models.IntegerField()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Try to use validator here

Comment thread cars/models.py Outdated
price = models.DecimalField(max_digits=12, decimal_places=2)
condition = models.CharField(max_length=20, choices=CAR_CONDITION_CHOICES)
description = models.TextField(blank=True)
is_deleted = models.BooleanField(default=False)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Use is_active for soft delete. This improvement was highlighted previously as well.

Comment thread cars/models.py Outdated
Comment on lines +43 to +47
indexes = [
models.Index(fields=['seller_id']),
models.Index(fields=['is_deleted']),
models.Index(fields=['model_id']),
]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why we have decided these specific indexes?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

These indexes were added based on expected query patterns seller_id for seller-based filtering, model_id for model-based searches/filtering, and is_active because soft delete filtering is frequently used.

Comment thread cars/models.py Outdated
is_deleted = models.BooleanField(default=False)

class Meta:
db_table = 'cars'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

no need to do this

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Resolved

Comment thread cars/models.py Outdated
car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name='images')
image = models.ImageField(upload_to='cars/images/')
is_primary = models.BooleanField(default=False)
display_order = models.IntegerField(default=0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

should not it be auto-generated but user can edit if want?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Resolved

Comment thread cars/mixins.py Outdated
Comment on lines +4 to +10
class CarQuerysetMixin:
def get_base_queryset(self):
return (
Car.objects.filter(is_deleted=False)
.select_related('model__brand', 'seller')
.prefetch_related('images')
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Try to create Soft delete mixin for reusable field in models/
And create custom manager for filtering query set

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Resolved

Comment thread cars/choices.py Outdated
@@ -0,0 +1,6 @@
CAR_CONDITION_CHOICES = [

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

use TextChoices instead

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Resolved

Comment thread cars/admin.py Outdated

@admin.register(Car)
class CarAdmin(admin.ModelAdmin):
list_display = ['id', 'model', 'seller', 'year', 'license_plate', 'city', 'price', 'condition', 'is_deleted', 'created', 'modified']

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

look into pep8 for improving readability here. look for how we can divide the list elements

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Resolved

Comment thread cars/managers.py Outdated
Comment on lines +18 to +22
def with_inactive(self):
return super().get_queryset()

def only_inactive(self):
return super().get_queryset().filter(is_active=False)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why we have created these methods when we are not using them anywhere?

Comment thread cars/managers.py
from django.db import models

from common.managers import ActiveQuerySet

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Can we use name "core" instead of common ?

Comment thread cars/models.py Outdated
Comment on lines +14 to +21
def current_year_plus_one():
return datetime.date.today().year + 1


def validate_car_year(value):
current_year = datetime.date.today().year
if value < 1886 or value > current_year + 1:
raise ValidationError(f"Year must be between 1886 and {current_year + 1}.")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why we have these methods in models.py ?

Comment thread cars/models.py
Comment on lines +60 to +70

year = models.IntegerField(
validators=[
MinValueValidator(1886),
MaxValueValidator(current_year_plus_one),
]
)

license_plate = models.CharField(max_length=20, unique=True)

city = models.CharField(max_length=100)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why we have these blank spaces?

Comment thread cars/views.py Outdated
filterset_class = CarFilter

def get_queryset(self):
return Car.objects.with_related()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

use proper naming instead of with_related

Renamed the common package to core and updated imports across the app. Extracted car year validation into a new cars/validators.py (validate_car_year) and replaced inline datetime/validator logic in the Car model. Switched imports to core.managers/core.model, renamed CarQuerySet.with_related to with_car_relations and removed the with_inactive/only_inactive helpers, and updated views to call the new queryset helper methods. These changes centralize shared utilities, enable validator reuse, and simplify the queryset API.
Introduce a SoftDeleteMixin and remove legacy core model/manager implementations. Deleted core/managers.py and core/model.py and added core/mixins.py with SoftDeleteMixin (adds is_active and soft_delete()). Update cars to use the new mixin: Car now inherits SoftDeleteMixin, CarQuerySet no longer depends on the removed ActiveQuerySet (uses models.QuerySet), and views now call with_car_relations() and car.soft_delete() instead of manual is_active toggling or with_optimized_queries(). This simplifies soft-delete behavior and decouples car query logic from the old core manager utilities.
Comment thread cars/validators.py Outdated
import datetime
from django.core.exceptions import ValidationError

def validate_car_year(value):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why we are not using serializer for it ?

Move year validation from a separate validator into CarCreateUpdateSerializer (validate_year) and remove cars/validators.py. Rename ImagesSerializer to ImageSerializer and update usages. Simplify and reformat filter and model field declarations for readability. Adjust permissions and view behavior: use class-level queryset and permission_classes (IsAuthenticatedOrReadOnly where appropriate), replace destroy override with perform_destroy calling soft_delete, and add proper object-permission checks for car image uploads (get_car + check_object_permissions). Minor cleanup in permissions and core mixin (whitespace/import formatting and safer user auth check).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants