generated from mwc/project_banjo_app
Imported recipes and ensured the they work by searching recipe name and ingredients.
I’m proud that importing recipes works and the ingredients, steps, and notes show up correctly. I also learned how to set up models and link them with views in Banjo, which was really helpful. This project taught me a lot about building a small app with models, views, and working APIs.
This commit is contained in:
0
project/app/_init_.py
Normal file
0
project/app/_init_.py
Normal file
BIN
project/app/database.sqlite
Normal file
BIN
project/app/database.sqlite
Normal file
Binary file not shown.
62
project/app/migrations/0001_initial.py
Normal file
62
project/app/migrations/0001_initial.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# Generated by Django 5.1.4 on 2026-03-22 21:35
|
||||
|
||||
import banjo.models
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Recipe',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', banjo.models.StringField(default='')),
|
||||
('url', banjo.models.StringField(default='')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Note',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('text', banjo.models.StringField(default='')),
|
||||
('creation', banjo.models.StringField(default='')),
|
||||
('recipe', banjo.models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='app.recipe')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Ingredient',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('text', banjo.models.StringField(default='')),
|
||||
('recipe', banjo.models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ingredients', to='app.recipe')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Step',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('text', banjo.models.StringField(default='')),
|
||||
('order', banjo.models.IntegerField(default=0)),
|
||||
('recipe', banjo.models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='steps', to='app.recipe')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
0
project/app/migrations/__init__.py
Normal file
0
project/app/migrations/__init__.py
Normal file
32
project/app/models.py
Normal file
32
project/app/models.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from banjo.models import Model, StringField, IntegerField, ForeignKey
|
||||
from datetime import datetime
|
||||
|
||||
class Recipe(Model):
|
||||
name = StringField()
|
||||
url = StringField()
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"name": self.name,
|
||||
"url": self.url,
|
||||
"ingredients": [i.text for i in self.ingredients.all()],
|
||||
"steps": [s.text for s in self.steps.order_by("order")],
|
||||
"notes": [n.to_dict() for n in self.notes.order_by("creation")],
|
||||
}
|
||||
|
||||
class Ingredient(Model):
|
||||
text = StringField()
|
||||
recipe = ForeignKey("Recipe", related_name="ingredients")
|
||||
|
||||
class Step(Model):
|
||||
text = StringField()
|
||||
order = IntegerField()
|
||||
recipe = ForeignKey("Recipe", related_name="steps")
|
||||
|
||||
class Note(Model):
|
||||
text = StringField()
|
||||
creation = StringField(default=lambda: datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||
recipe = ForeignKey("Recipe", related_name="notes")
|
||||
|
||||
def to_dict(self):
|
||||
return {"date": self.creation, "note": self.text}
|
||||
58
project/app/views.py
Normal file
58
project/app/views.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from app.models import Recipe, Ingredient, Step, Note
|
||||
from banjo.http import BadRequest
|
||||
from banjo.urls import route_get, route_post
|
||||
from scrape_schema_recipe import scrape_url
|
||||
from datetime import datetime
|
||||
|
||||
@route_get('recipes/all')
|
||||
def show_all_recipes(params):
|
||||
"""Show all recipes"""
|
||||
recipes = Recipe.objects.all()
|
||||
return {'recipes': [r.to_dict() for r in recipes]}
|
||||
|
||||
@route_get('recipes/search', args={'query': str})
|
||||
def search_for_recipe(params):
|
||||
"""Search recipes by name"""
|
||||
recipes = Recipe.objects.filter(name__icontains=params['query'])
|
||||
return {'recipes': [r.to_dict() for r in recipes]}
|
||||
|
||||
@route_get('recipes/search-ingredient', args={'query': str})
|
||||
def search_for_recipe_by_ingredient(params):
|
||||
"""Search recipes by ingredient"""
|
||||
recipes = Recipe.objects.filter(ingredients__text__icontains=params['query'])
|
||||
return {'recipes': [r.to_dict() for r in recipes]}
|
||||
|
||||
@route_post('recipes/import', args={'url': str})
|
||||
def import_recipe(params):
|
||||
"""Import a recipe from a URL"""
|
||||
url = params['url']
|
||||
if Recipe.objects.filter(url=url).exists():
|
||||
raise BadRequest(f"{url} has already been imported.")
|
||||
|
||||
try:
|
||||
scraped_recipes = scrape_url(url)
|
||||
except Exception as e:
|
||||
raise BadRequest(f"Error reading {url}: {e}")
|
||||
|
||||
if len(scraped_recipes) == 0:
|
||||
raise BadRequest(f"No recipes found at {url}")
|
||||
|
||||
recipes_created = []
|
||||
for r in scraped_recipes:
|
||||
recipe = Recipe(name=r['name'], url=url)
|
||||
recipe.save()
|
||||
for i in r.get('recipeIngredient', []):
|
||||
Ingredient(recipe=recipe, text=i).save()
|
||||
for idx, s in enumerate(r.get('recipeInstructions', []), start=1):
|
||||
Step(recipe=recipe, text=s['text'], order=idx).save()
|
||||
recipes_created.append(recipe.to_dict())
|
||||
|
||||
return {'recipes': recipes_created}
|
||||
|
||||
@route_post("recipes/add-note", args={'recipe_id': int, 'note': str})
|
||||
def add_note(params):
|
||||
"""Add a note to a recipe"""
|
||||
recipe = Recipe.objects.get(id=params['recipe_id'])
|
||||
note = Note(recipe=recipe, text=params['note'])
|
||||
note.save()
|
||||
return recipe.to_dict()
|
||||
Reference in New Issue
Block a user