endpoint, frontend

This commit is contained in:
Bartosz Karwacki 2022-01-15 17:40:16 +01:00
parent 6a35148188
commit 8e29a715a5
12 changed files with 168 additions and 87 deletions

View File

@ -50,6 +50,7 @@ INSTALLED_APPS = [
"corsheaders", "corsheaders",
"django_filters", "django_filters",
# apps # apps
'cars'
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@ -154,7 +155,4 @@ DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
# CORS # CORS
CORS_ALLOWED_ORIGINS = env( CORS_ALLOW_ALL_ORIGINS = True
"ALLOWED_ORIGINS",
default="http://localhost:3000",
).split()

View File

@ -0,0 +1,27 @@
# Generated by Django 3.2.9 on 2022-01-15 15:58
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Car',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('mark', models.CharField(max_length=150)),
('model', models.CharField(max_length=150)),
('mileage', models.PositiveIntegerField()),
('production_year', models.PositiveSmallIntegerField()),
('engine_capacity', models.DecimalField(decimal_places=2, max_digits=16)),
('combustion', models.DecimalField(decimal_places=2, max_digits=16)),
('price', models.DecimalField(decimal_places=2, max_digits=16)),
],
),
]

View File

@ -5,9 +5,9 @@ class Car(models.Model):
model = models.CharField(max_length=150) model = models.CharField(max_length=150)
mileage = models.PositiveIntegerField() mileage = models.PositiveIntegerField()
production_year = models.PositiveSmallIntegerField() production_year = models.PositiveSmallIntegerField()
engine_capacity = models.DecimalField() engine_capacity = models.DecimalField(decimal_places=2, max_digits=16)
combustion = models.DecimalField() combustion = models.DecimalField(decimal_places=2, max_digits=16)
price = models.DecimalField() price = models.DecimalField(decimal_places=2, max_digits=16)
def __str__(self) -> str: def __str__(self) -> str:
return "Car" return "Car"

View File

@ -1,12 +1,7 @@
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from .views import CarList
from .views import CarViews
# Create a router and register our viewsets with it.
router = DefaultRouter()
router.register(r'cars', CarViews.as_view(), name="cars")
# The API URLs are now determined automatically by the router. # The API URLs are now determined automatically by the router.
urlpatterns = [ urlpatterns = [
path('', include(router.urls)), path('cars', CarList.as_view()),
] ]

25
backend/cars/utils.py Normal file
View File

@ -0,0 +1,25 @@
VALUES = ['low', 'mid', 'high']
def production_year_to_value(production_year):
return VALUES[["Dawno", "Średnio", "Niedawno"].index(production_year)]
def mileage_to_value(mileage):
return VALUES[["Niski", "Średni", "Duży"].index(mileage)]
def engine_capacity_to_value(engine_capacity):
return VALUES[["Mała", "Średnia", "Ogromna"].index(engine_capacity)]
def combustion_to_value(combustion):
return VALUES[["Niskie", "Średnie", "Wysokie"].index(combustion)]
def map_query_params(query_params):
values = {}
params = ['production_year', 'mileage', 'engine_capacity', 'combustion']
funcs = [production_year_to_value, mileage_to_value, engine_capacity_to_value, combustion_to_value]
for param, func in zip(params, funcs):
if (query_param := query_params.get(param)):
values[param] = func(query_param)
return values

View File

@ -1,19 +1,15 @@
from rest_framework.views import APIView from rest_framework import generics
from .models import Car
from rest_framework.response import Response from rest_framework.response import Response
from .models import Car
from .serializers import CarSerializer
class CarViews(APIView): from .utils import map_query_params
def get(self, request, format=None): class CarList(generics.ListAPIView):
""" queryset = Car.objects.all()
Returns car list based on fuzzy_logic filters. serializer_class = CarSerializer
"""
combustion = request.query_params.get('combustion')
mileage = request.query_params.get('mileage') def list(self, request):
production_year = request.query_params.get('production_year') values = map_query_params(request.query_params)
engine_capacity = request.query_params.get('engine_capacity') queryset = self.get_queryset()
serializer = CarSerializer(queryset, many=True)
print(combustion, mileage, production_year, engine_capacity) return Response(serializer.data)
cars = list(Car.objects.all())
return Response(cars)

View File

@ -1,3 +1,4 @@
SECRET_KEY=SECRET_KEY SECRET_KEY=SECRET_KEY
DEBUG=1 DEBUG=1
ALLOWED_HOSTS=localhost 127.0.0.1 ALLOWED_HOSTS=localhost 127.0.0.1
ALLOWED_ORIGINS=http://localhost:8080 http://127.0.01:8080

View File

@ -7,6 +7,7 @@
"": { "": {
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"axios": "^0.24.0",
"core-js": "^3.6.5", "core-js": "^3.6.5",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-router": "^3.2.0", "vue-router": "^3.2.0",
@ -2610,7 +2611,6 @@
"thread-loader": "^2.1.3", "thread-loader": "^2.1.3",
"url-loader": "^2.2.0", "url-loader": "^2.2.0",
"vue-loader": "^15.9.2", "vue-loader": "^15.9.2",
"vue-loader-v16": "npm:vue-loader@^16.1.0",
"vue-style-loader": "^4.1.2", "vue-style-loader": "^4.1.2",
"webpack": "^4.0.0", "webpack": "^4.0.0",
"webpack-bundle-analyzer": "^3.8.0", "webpack-bundle-analyzer": "^3.8.0",
@ -3394,6 +3394,14 @@
"integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
"dev": true "dev": true
}, },
"node_modules/axios": {
"version": "0.24.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
"dependencies": {
"follow-redirects": "^1.14.4"
}
},
"node_modules/babel-code-frame": { "node_modules/babel-code-frame": {
"version": "6.26.0", "version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
@ -3711,7 +3719,6 @@
"@types/node": "*", "@types/node": "*",
"anymatch": "^3.0.3", "anymatch": "^3.0.3",
"fb-watchman": "^2.0.0", "fb-watchman": "^2.0.0",
"fsevents": "^2.3.2",
"graceful-fs": "^4.2.4", "graceful-fs": "^4.2.4",
"jest-regex-util": "^27.0.6", "jest-regex-util": "^27.0.6",
"jest-serializer": "^27.0.6", "jest-serializer": "^27.0.6",
@ -4862,7 +4869,6 @@
"dependencies": { "dependencies": {
"anymatch": "~3.1.2", "anymatch": "~3.1.2",
"braces": "~3.0.2", "braces": "~3.0.2",
"fsevents": "~2.3.2",
"glob-parent": "~5.1.2", "glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0", "is-binary-path": "~2.1.0",
"is-glob": "~4.0.1", "is-glob": "~4.0.1",
@ -7207,8 +7213,7 @@
"esprima": "^4.0.1", "esprima": "^4.0.1",
"estraverse": "^4.2.0", "estraverse": "^4.2.0",
"esutils": "^2.0.2", "esutils": "^2.0.2",
"optionator": "^0.8.1", "optionator": "^0.8.1"
"source-map": "~0.6.1"
}, },
"bin": { "bin": {
"escodegen": "bin/escodegen.js", "escodegen": "bin/escodegen.js",
@ -8224,7 +8229,6 @@
"version": "1.14.5", "version": "1.14.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==",
"dev": true,
"engines": { "engines": {
"node": ">=4.0" "node": ">=4.0"
} }
@ -10509,7 +10513,6 @@
"@jest/types": "^24.9.0", "@jest/types": "^24.9.0",
"anymatch": "^2.0.0", "anymatch": "^2.0.0",
"fb-watchman": "^2.0.0", "fb-watchman": "^2.0.0",
"fsevents": "^1.2.7",
"graceful-fs": "^4.1.15", "graceful-fs": "^4.1.15",
"invariant": "^2.2.4", "invariant": "^2.2.4",
"jest-serializer": "^24.9.0", "jest-serializer": "^24.9.0",
@ -18092,8 +18095,7 @@
"dependencies": { "dependencies": {
"chokidar": "^3.4.1", "chokidar": "^3.4.1",
"graceful-fs": "^4.1.2", "graceful-fs": "^4.1.2",
"neo-async": "^2.5.0", "neo-async": "^2.5.0"
"watchpack-chokidar2": "^2.0.1"
}, },
"optionalDependencies": { "optionalDependencies": {
"watchpack-chokidar2": "^2.0.1" "watchpack-chokidar2": "^2.0.1"
@ -18467,7 +18469,6 @@
"anymatch": "^2.0.0", "anymatch": "^2.0.0",
"async-each": "^1.0.1", "async-each": "^1.0.1",
"braces": "^2.3.2", "braces": "^2.3.2",
"fsevents": "^1.2.7",
"glob-parent": "^3.1.0", "glob-parent": "^3.1.0",
"inherits": "^2.0.3", "inherits": "^2.0.3",
"is-binary-path": "^1.0.0", "is-binary-path": "^1.0.0",
@ -22093,6 +22094,14 @@
"integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
"dev": true "dev": true
}, },
"axios": {
"version": "0.24.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
"requires": {
"follow-redirects": "^1.14.4"
}
},
"babel-code-frame": { "babel-code-frame": {
"version": "6.26.0", "version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
@ -26185,8 +26194,7 @@
"follow-redirects": { "follow-redirects": {
"version": "1.14.5", "version": "1.14.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA=="
"dev": true
}, },
"for-in": { "for-in": {
"version": "1.0.2", "version": "1.0.2",

View File

@ -9,6 +9,7 @@
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"axios": "^0.24.0",
"core-js": "^3.6.5", "core-js": "^3.6.5",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-router": "^3.2.0", "vue-router": "^3.2.0",

View File

@ -1,48 +1,63 @@
<template> <template>
<v-container> <v-container>
<h1 class="title font-weight-thin">Wyszukiwarka samochodów</h1> <h1 class="title font-weight-thin">Wyszukiwarka samochodów</h1>
<v-row> <v-row>
<v-col md="3" v-for="item in items" :key="item.label"> <v-col md="3" v-for="item in items" :key="item.label">
<v-combobox v-model="item.selected" :items="item.items" :label="item.label"></v-combobox> <v-combobox
</v-col> v-model="item.selected"
</v-row> :items="item.items"
<v-row justify="center"> :label="item.label"
<v-btn rounded color="primary" dark>Szukaj</v-btn> ></v-combobox>
</v-row> </v-col>
</v-container> </v-row>
<v-row justify="center">
<v-btn rounded color="primary" dark @click="send">Szukaj</v-btn>
</v-row>
</v-container>
</template> </template>
<script> <script>
import axios from "axios";
import { mapMutations } from 'vuex'
export default { export default {
name: "Search", name: "Search",
data: () => ({ data: () => ({
items: { items: {
age: { production_year: {
label: "Rok produkcji", label: "Rok produkcji",
selected: "", selected: "",
items: ["Dawno", "Średnio", "Niedawno"], items: ["Dawno", "Średnio", "Niedawno"],
}, },
mileage: { mileage: {
label: "Przebieg", label: "Przebieg",
selected: "", selected: "",
items: ["Niski", "Średni", "Duży"], items: ["Niski", "Średni", "Duży"],
}, },
engine_capacity: { engine_capacity: {
label: "Pojemność silnika", label: "Pojemność silnika",
selected: "", selected: "",
items: ["Mała", "Średnia", "Ogromna"], items: ["Mała", "Średnia", "Ogromna"],
}, },
fuel_usage: { combustion: {
label: "Spalanie paliwa", label: "Spalanie paliwa",
selected: "", selected: "",
items: ["Niskie", "Średnie", "Wysokie"], items: ["Niskie", "Średnie", "Wysokie"],
}, },
}, },
}), }),
methods: {
...mapMutations(['setCars']),
send() {
let query = {}
Object.entries(this.items).forEach(([key, value]) =>
query[key] = value.selected
);
axios.get('http://localhost:8000/cars', { params: query}).then((res) => { this.setCars(res.data) })
},
},
}; };
</script> </script>
<style scoped> <style scoped>
</style> </style>

View File

@ -2,13 +2,14 @@
<v-container> <v-container>
<v-row> <v-row>
<v-col md="12"> <v-col md="12">
<v-data-table :headers="headers" :items="items" class="elevation-1"></v-data-table> <v-data-table :headers="headers" :items="getCars" class="elevation-1"></v-data-table>
</v-col> </v-col>
</v-row> </v-row>
</v-container> </v-container>
</template> </template>
<script> <script>
import { mapGetters } from 'vuex'
export default { export default {
name: "Table", name: "Table",
@ -19,7 +20,7 @@ export default {
{ text: "Przebieg (km)", value: "mileage" }, { text: "Przebieg (km)", value: "mileage" },
{ text: "Rok produkcji", value: "production_year" }, { text: "Rok produkcji", value: "production_year" },
{ text: "Pojemnośc silnika (cm^3)", value: "engine_capacity" }, { text: "Pojemnośc silnika (cm^3)", value: "engine_capacity" },
{ text: "Spalanie (L/100km)", value: "fuel_usage" }, { text: "Spalanie (L/100km)", value: "combustion" },
{ text: "Cena (zł)", value: "price" }, { text: "Cena (zł)", value: "price" },
], ],
items: [{ items: [{
@ -28,7 +29,7 @@ export default {
mileage: 230000, mileage: 230000,
production_year: 2008, production_year: 2008,
engine_capacity: 2.0, engine_capacity: 2.0,
fuel_usage: 7.6, combustion: 7.6,
price: 12200, price: 12200,
}, },
{ {
@ -37,11 +38,14 @@ export default {
mileage: 430000, mileage: 430000,
production_year: 2003, production_year: 2003,
engine_capacity: 1.3, engine_capacity: 1.3,
fuel_usage: 5.5, combustion: 5.5,
price: 3500, price: 3500,
}, },
], ],
}), }),
computed: {
...mapGetters(['getCars'])
}
}; };
</script> </script>

View File

@ -4,8 +4,19 @@ import Vuex from "vuex";
Vue.use(Vuex); Vue.use(Vuex);
export default new Vuex.Store({ export default new Vuex.Store({
state: {}, state: {
mutations: {}, cars: []
},
mutations: {
setCars(state, cars) {
state.cars = cars
}
},
getters: {
getCars: state => {
return state.cars
}
},
actions: {}, actions: {},
modules: {}, modules: {},
}); });