From 3f6417d3cf99c79b1a379170e5e8ea6872ce69b4 Mon Sep 17 00:00:00 2001 From: Lucas Souza Date: Wed, 1 Feb 2023 16:22:20 -0300 Subject: [PATCH] feat: form to place bids --- README.md | 24 ++++++------ auctions/forms.py | 18 +++++++++ auctions/migrations/0001_initial.py | 17 ++++++++- .../migrations/0002_alter_biding_options.py | 17 +++++++++ auctions/migrations/0002_biding.py | 24 ------------ .../migrations/0003_alter_biding_options.py | 17 +++++++++ .../0003_listing_photo_alter_biding_value.py | 23 ------------ ...ing_title_remove_listing_photo_and_more.py | 37 ------------------- auctions/models.py | 12 +++--- auctions/templates/auctions/add.html | 2 - auctions/templates/auctions/index.html | 2 +- auctions/templates/auctions/listing.html | 13 ++++++- auctions/urls.py | 3 +- auctions/views.py | 30 ++++++++++++++- 14 files changed, 128 insertions(+), 111 deletions(-) create mode 100644 auctions/migrations/0002_alter_biding_options.py delete mode 100644 auctions/migrations/0002_biding.py create mode 100644 auctions/migrations/0003_alter_biding_options.py delete mode 100644 auctions/migrations/0003_listing_photo_alter_biding_value.py delete mode 100644 auctions/migrations/0004_rename_name_listing_title_remove_listing_photo_and_more.py diff --git a/README.md b/README.md index ecdb195..e1e2f41 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,15 @@ Design an eBay-like e-commerce auction site that will allow users to post auctio ## Specification Complete the implementation of your auction site. You must fulfill the following requirements: - - Models: Your application should have at least three models in addition to the User model: one for auction listings, one for bids, and one for comments made on auction listings. It’s up to you to decide what fields each model should have, and what the types of those fields should be. You may have additional models if you would like. - - Create Listing: Users should be able to visit a page to create a new listing. They should be able to specify a title for the listing, a text-based description, and what the starting bid should be. Users should also optionally be able to provide a URL for an image for the listing and/or a category (e.g. Fashion, Toys, Electronics, Home, etc.). - - Active Listings Page: The default route of your web application should let users view all of the currently active auction listings. For each active listing, this page should display (at minimum) the title, description, current price, and photo (if one exists for the listing). - - Listing Page: Clicking on a listing should take users to a page specific to that listing. On that page, users should be able to view all details about the listing, including the current price for the listing. - - If the user is signed in, the user should be able to add the item to their “Watchlist.” If the item is already on the watchlist, the user should be able to remove it. - - If the user is signed in, the user should be able to bid on the item. The bid must be at least as large as the starting bid, and must be greater than any other bids that have been placed (if any). If the bid doesn’t meet those criteria, the user should be presented with an error. - - If the user is signed in and is the one who created the listing, the user should have the ability to “close” the auction from this page, which makes the highest bidder the winner of the auction and makes the listing no longer active. - - If a user is signed in on a closed listing page, and the user has won that auction, the page should say so. - - Users who are signed in should be able to add comments to the listing page. The listing page should display all comments that have been made on the listing. - - Watchlist: Users who are signed in should be able to visit a Watchlist page, which should display all of the listings that a user has added to their watchlist. Clicking on any of those listings should take the user to that listing’s page. - - Categories: Users should be able to visit a page that displays a list of all listing categories. Clicking on the name of any category should take the user to a page that displays all of the active listings in that category. - - Django Admin Interface: Via the Django admin interface, a site administrator should be able to view, add, edit, and delete any listings, comments, and bids made on the site. + - [ ] Models: Your application should have at least three models in addition to the User model: one for auction listings, one for bids, and one for comments made on auction listings. It’s up to you to decide what fields each model should have, and what the types of those fields should be. You may have additional models if you would like. + - [x] Create Listing: Users should be able to visit a page to create a new listing. They should be able to specify a title for the listing, a text-based description, and what the starting bid should be. Users should also optionally be able to provide a URL for an image for the listing and/or a category (e.g. Fashion, Toys, Electronics, Home, etc.). + - [ ] Active Listings Page: The default route of your web application should let users view all of the currently active auction listings. For each active listing, this page should display (at minimum) the title, description, current price, and photo (if one exists for the listing). + - [ ] Listing Page: Clicking on a listing should take users to a page specific to that listing. On that page, users should be able to view all details about the listing, including the current price for the listing. + - [ ] If the user is signed in, the user should be able to add the item to their “Watchlist.” If the item is already on the watchlist, the user should be able to remove it. + - [ ] If the user is signed in, the user should be able to bid on the item. The bid must be at least as large as the starting bid, and must be greater than any other bids that have been placed (if any). If the bid doesn’t meet those criteria, the user should be presented with an error. + - [ ] If the user is signed in and is the one who created the listing, the user should have the ability to “close” the auction from this page, which makes the highest bidder the winner of the auction and makes the listing no longer active. + - [ ] If a user is signed in on a closed listing page, and the user has won that auction, the page should say so. + - [ ] Users who are signed in should be able to add comments to the listing page. The listing page should display all comments that have been made on the listing. + - [ ] Watchlist: Users who are signed in should be able to visit a Watchlist page, which should display all of the listings that a user has added to their watchlist. Clicking on any of those listings should take the user to that listing’s page. + - [ ] Categories: Users should be able to visit a page that displays a list of all listing categories. Clicking on the name of any category should take the user to a page that displays all of the active listings in that category. + - [ ] Django Admin Interface: Via the Django admin interface, a site administrator should be able to view, add, edit, and delete any listings, comments, and bids made on the site. diff --git a/auctions/forms.py b/auctions/forms.py index 970e002..635e519 100644 --- a/auctions/forms.py +++ b/auctions/forms.py @@ -6,3 +6,21 @@ class NewListing(forms.ModelForm): class Meta: model = Listing fields = ['title', 'description', 'category', 'photo_url', 'initial_bid'] + + +class NewBid(forms.ModelForm): + def __init__(self, *args, **kwargs): + print(kwargs) + self.item = kwargs.pop('item') + super(NewBid, self).__init__(*args, **kwargs) + + def clean(self): + value = self.cleaned_data['value'] + if value <= self.item.price: + raise forms.ValidationError("Your bid is lower than current price") + + return self.cleaned_data + + class Meta: + model = Biding + fields = ['value'] diff --git a/auctions/migrations/0001_initial.py b/auctions/migrations/0001_initial.py index aecc039..9a55a53 100644 --- a/auctions/migrations/0001_initial.py +++ b/auctions/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.1.5 on 2023-01-31 19:09 +# Generated by Django 4.1.5 on 2023-02-01 18:21 from django.conf import settings import django.contrib.auth.models @@ -47,9 +47,22 @@ class Migration(migrations.Migration): name='Listing', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=64)), + ('title', models.CharField(max_length=64)), ('description', models.CharField(max_length=256)), + ('initial_bid', models.DecimalField(decimal_places=2, default=0.0, max_digits=6)), + ('photo_url', models.URLField(blank=True)), + ('category', models.CharField(blank=True, choices=[('Fashion', 'Fashion'), ('Home', 'Home'), ('Electronics', 'Electronics'), ('Toys', 'Toys'), ('Other', 'Other')], max_length=24)), + ('price', models.DecimalField(blank=True, decimal_places=2, max_digits=6)), ('seller', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='listings', to=settings.AUTH_USER_MODEL)), ], ), + migrations.CreateModel( + name='Biding', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('value', models.DecimalField(decimal_places=2, max_digits=6)), + ('buyer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bidings', to=settings.AUTH_USER_MODEL)), + ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bidings', to='auctions.listing')), + ], + ), ] diff --git a/auctions/migrations/0002_alter_biding_options.py b/auctions/migrations/0002_alter_biding_options.py new file mode 100644 index 0000000..da15789 --- /dev/null +++ b/auctions/migrations/0002_alter_biding_options.py @@ -0,0 +1,17 @@ +# Generated by Django 4.1.5 on 2023-02-01 18:56 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('auctions', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='biding', + options={'ordering': ['value']}, + ), + ] diff --git a/auctions/migrations/0002_biding.py b/auctions/migrations/0002_biding.py deleted file mode 100644 index 98c0f1f..0000000 --- a/auctions/migrations/0002_biding.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 4.1.5 on 2023-01-31 19:29 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('auctions', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='Biding', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('value', models.FloatField()), - ('buyer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bidings', to=settings.AUTH_USER_MODEL)), - ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bidings', to='auctions.listing')), - ], - ), - ] diff --git a/auctions/migrations/0003_alter_biding_options.py b/auctions/migrations/0003_alter_biding_options.py new file mode 100644 index 0000000..7a8d0c4 --- /dev/null +++ b/auctions/migrations/0003_alter_biding_options.py @@ -0,0 +1,17 @@ +# Generated by Django 4.1.5 on 2023-02-01 19:14 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('auctions', '0002_alter_biding_options'), + ] + + operations = [ + migrations.AlterModelOptions( + name='biding', + options={}, + ), + ] diff --git a/auctions/migrations/0003_listing_photo_alter_biding_value.py b/auctions/migrations/0003_listing_photo_alter_biding_value.py deleted file mode 100644 index 795e94e..0000000 --- a/auctions/migrations/0003_listing_photo_alter_biding_value.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 4.1.5 on 2023-01-31 19:37 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('auctions', '0002_biding'), - ] - - operations = [ - migrations.AddField( - model_name='listing', - name='photo', - field=models.ImageField(blank=True, upload_to='photos/'), - ), - migrations.AlterField( - model_name='biding', - name='value', - field=models.DecimalField(decimal_places=2, max_digits=6), - ), - ] diff --git a/auctions/migrations/0004_rename_name_listing_title_remove_listing_photo_and_more.py b/auctions/migrations/0004_rename_name_listing_title_remove_listing_photo_and_more.py deleted file mode 100644 index b396bff..0000000 --- a/auctions/migrations/0004_rename_name_listing_title_remove_listing_photo_and_more.py +++ /dev/null @@ -1,37 +0,0 @@ -# Generated by Django 4.1.5 on 2023-01-31 19:56 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('auctions', '0003_listing_photo_alter_biding_value'), - ] - - operations = [ - migrations.RenameField( - model_name='listing', - old_name='name', - new_name='title', - ), - migrations.RemoveField( - model_name='listing', - name='photo', - ), - migrations.AddField( - model_name='listing', - name='category', - field=models.CharField(blank=True, choices=[('Fashion', 'Fashion'), ('Home', 'Home'), ('Electronics', 'Electronics'), ('Toys', 'Toys'), ('Other', 'Other')], max_length=24), - ), - migrations.AddField( - model_name='listing', - name='initial_bid', - field=models.DecimalField(decimal_places=2, default=0.0, max_digits=6), - ), - migrations.AddField( - model_name='listing', - name='photo_url', - field=models.URLField(blank=True), - ), - ] diff --git a/auctions/models.py b/auctions/models.py index 6d4eedb..5c7539b 100644 --- a/auctions/models.py +++ b/auctions/models.py @@ -7,11 +7,6 @@ class User(AbstractUser): class Listing(models.Model): - title = models.CharField(max_length=64) - description = models.CharField(max_length=256) - initial_bid = models.DecimalField(max_digits=6, decimal_places=2, default=0.00) - photo_url = models.URLField(blank=True) - CATEGORIES_CHOICES = [ ('Fashion', 'Fashion'), ('Home', 'Home'), @@ -19,9 +14,14 @@ class Listing(models.Model): ('Toys', 'Toys'), ('Other', 'Other'), ] - category = models.CharField(choices=CATEGORIES_CHOICES, max_length=24, blank=True) + title = models.CharField(max_length=64) + description = models.CharField(max_length=256) + initial_bid = models.DecimalField(max_digits=6, decimal_places=2, default=0.00) + photo_url = models.URLField(blank=True) + category = models.CharField(choices=CATEGORIES_CHOICES, max_length=24, blank=True) seller = models.ForeignKey(User, on_delete=models.CASCADE, related_name="listings") + price = models.DecimalField(max_digits=6, decimal_places=2, blank=True) def __str__(self): return f"{self.title}: {self.description}" diff --git a/auctions/templates/auctions/add.html b/auctions/templates/auctions/add.html index 236d976..36a9eec 100644 --- a/auctions/templates/auctions/add.html +++ b/auctions/templates/auctions/add.html @@ -8,6 +8,4 @@

New Listing

{{ form }} - - All Listings {% endblock %} \ No newline at end of file diff --git a/auctions/templates/auctions/index.html b/auctions/templates/auctions/index.html index faeb793..8e92b9b 100644 --- a/auctions/templates/auctions/index.html +++ b/auctions/templates/auctions/index.html @@ -8,7 +8,7 @@

Active Listings

  • {{ listing.title }}: - {{ listing.description }}, by {{ listing.seller }} + {{ listing.description }}, by {{ listing.seller }} - $ {{ listing.price }}
  • {% endfor %} diff --git a/auctions/templates/auctions/listing.html b/auctions/templates/auctions/listing.html index 1d3a7e1..7591b3c 100644 --- a/auctions/templates/auctions/listing.html +++ b/auctions/templates/auctions/listing.html @@ -8,9 +8,20 @@

    {{ listing.title }}

  • Seller: {{ listing.seller }}
  • {% if listing.category %}
  • Category: {{ listing.category }}
  • + {% else %} +
  • Category: Category not listed
  • {% endif %} +
  • Price: {{ listing.price }}
  • +

    Place bid

    +
    + {% csrf_token %} + {{ form }} + +
    + +

    Bidings

    - - All Listings {% endblock %} \ No newline at end of file diff --git a/auctions/urls.py b/auctions/urls.py index 3b6d731..26fc778 100644 --- a/auctions/urls.py +++ b/auctions/urls.py @@ -7,6 +7,7 @@ path("login", views.login_view, name="login"), path("logout", views.logout_view, name="logout"), path("register", views.register, name="register"), + path("add", views.add_listing, name="add"), path("", views.listing, name="listing"), - path("add", views.add_listing, name="add") + path("/bid", views.place_bid, name="bid") ] diff --git a/auctions/views.py b/auctions/views.py index 3f04ade..2c6abb5 100644 --- a/auctions/views.py +++ b/auctions/views.py @@ -71,9 +71,11 @@ def register(request): def listing(request, listing_id): auction = Listing.objects.get(id=listing_id) bidings = auction.bidings.all() + return render(request, "auctions/listing.html", { "listing": auction, - "bidings": bidings + "bidings": bidings, + "form": forms.NewBid(item=auction) }) @@ -84,6 +86,7 @@ def add_listing(request): if form.is_valid(): obj = form.save(commit=False) obj.seller = request.user + obj.price = obj.initial_bid obj.save() return HttpResponseRedirect(reverse("index")) else: @@ -92,3 +95,28 @@ def add_listing(request): return render(request, "auctions/add.html", { "form": form }) + + +@login_required +def place_bid(request, listing_id): + if request.method == "POST": + item = Listing.objects.get(id=listing_id) + form = forms.NewBid(request.POST, item=item) + + if form.is_valid(): + obj = form.save(commit=False) + obj.buyer = request.user + obj.item = item + obj.save() + + item.price = obj.value + item.save() + + return HttpResponseRedirect(reverse("listing", kwargs={'listing_id': listing_id})) + + else: + return render(request, "auctions/listing.html", { + "listing": item, + "bidings": item.bidings.all(), + "form": form + }) \ No newline at end of file