Squashed commit of the following:

commit 707d862e01a7497a1f22879d314b865a35e0e85b
Author: smilerz <smilerz@gmail.com>
Date:   Wed Apr 14 10:35:00 2021 -0500

    works now

commit 3942a445ed4f2ccec57de25eacd86ea4e4dd6bdb
Author: smilerz <smilerz@gmail.com>
Date:   Wed Apr 14 10:25:24 2021 -0500

    updated serializer and api

commit 10dc746eb175c7f805a8a8ffa7ce49977a7ce97e
Author: smilerz <smilerz@gmail.com>
Date:   Wed Apr 14 10:20:19 2021 -0500

    fixed bookmarklet

commit 9779104902d3be0258c95cd2eeebcba0d5d48892
Merge: bb8262c 0cb3928
Author: smilerz <smilerz@gmail.com>
Date:   Wed Apr 14 09:56:27 2021 -0500

    Merge branch 'bookmarklet' into json_import

commit 0cb39284bb835ffc6cfee3e4306aadc4a64a25be
Author: smilerz <smilerz@gmail.com>
Date:   Wed Apr 14 09:42:53 2021 -0500

    retrieve bookmarklet ID from get

commit e89e0218de684d40b2e2bfb6ba833891206c828e
Author: smilerz <smilerz@gmail.com>
Date:   Wed Apr 14 09:29:33 2021 -0500

    Revert "fixed broken tab"

    This reverts commit ca0a1aede3cc6cb3912bc1fe30c0aa22e3f481a6.

commit bb8262ccabb93c56fbc18c407d5a0653b8b3ca79
Merge: b1e73aa 35a7f62
Author: smilerz <smilerz@gmail.com>
Date:   Sun Apr 11 20:35:57 2021 -0500

    Merge branch 'main_fork' into json_import
This commit is contained in:
smilerz 2021-04-14 10:35:52 -05:00
parent 2c5348fcb4
commit faf458e8ef
17 changed files with 84 additions and 445 deletions

View File

@ -223,15 +223,15 @@ class ImportLogAdmin(admin.ModelAdmin):
admin.site.register(ImportLog, ImportLogAdmin)
class BookmarkletImportAdmin(admin.ModelAdmin):
list_display = ('id', 'url', 'created_by', 'created_at',)
admin.site.register(BookmarkletImport, BookmarkletImportAdmin)
class TelegramBotAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'created_by',)
admin.site.register(TelegramBot, TelegramBotAdmin)
class BookmarkletImportAdmin(admin.ModelAdmin):
list_display = ('id', 'url', 'created_by', 'created_at',)
admin.site.register(BookmarkletImport, BookmarkletImportAdmin)

View File

@ -61,6 +61,7 @@ with scopes_disabled():
model = Recipe
fields = ['name', 'keywords', 'foods', 'internal']
class FoodFilter(django_filters.FilterSet):
name = django_filters.CharFilter(lookup_expr='icontains')
@ -68,6 +69,7 @@ with scopes_disabled():
model = Food
fields = ['name']
class ShoppingListFilter(django_filters.FilterSet):
def __init__(self, data=None, *args, **kwargs):

View File

@ -73,9 +73,11 @@ def get_recipe_from_source(text, url, space):
html_data = []
images = []
# text = normalize_string(text)
try:
parse_list.append(remove_graph(json.loads(text)))
if not url and 'url' in parse_list[0]:
url = parse_list[0]['url']
scrape = text_scraper("<script type='application/ld+json'>" + text + "</script>", url=url)
except JSONDecodeError:
soup = BeautifulSoup(text, "html.parser")
@ -83,6 +85,8 @@ def get_recipe_from_source(text, url, space):
images += get_images_from_source(soup, url)
for el in soup.find_all('script', type='application/ld+json'):
el = remove_graph(el)
if not url and 'url' in el:
url = el['url']
if type(el) == list:
for le in el:
parse_list.append(le)
@ -95,15 +99,6 @@ def get_recipe_from_source(text, url, space):
parse_list.append(le)
elif type(el) == dict:
parse_list.append(el)
# if a url was not provided, try to find one in the first document
if not url and len(parse_list) > 0:
if 'url' in parse_list[0]:
url = parse_list[0]['url']
if type(text) == dict:
scrape = text_scraper("<script type='application/ld+json'>" + text + "</script>", url=url)
elif type(text) == str:
scrape = text_scraper(text, url=url)
recipe_json = helper.get_from_scraper(scrape, space)

View File

@ -39,9 +39,9 @@ def get_from_scraper(scrape, space):
pass
try:
recipe_json['image'] = scrape.image()
except AttributeError:
pass
recipe_json['image'] = parse_image(scrape.image()) or ''
except (AttributeError, TypeError):
recipe_json['image'] = ''
keywords = []
try:
@ -282,8 +282,8 @@ def parse_keywords(keyword_json, space):
# keywords as list
for kw in keyword_json:
kw = normalize_string(kw)
if len(kw) != 0:
if k := Keyword.objects.filter(name=kw, space=space).first():
if len(k) != 0:
keywords.append({'id': str(k.id), 'text': str(k)})
else:
keywords.append({'id': random.randrange(1111111, 9999999, 1), 'text': kw})

View File

@ -30,7 +30,7 @@ def text_scraper(text, url=None):
url=None
):
self.wild_mode = False
self.exception_handling = _exception_handling
self.exception_handling = None
self.meta_http_equiv = False
self.soup = BeautifulSoup(page_data, "html.parser")
self.url = url

View File

@ -14,8 +14,8 @@ from cookbook.models import (Comment, CookLog, Food, Ingredient, Keyword,
RecipeBook, RecipeBookEntry, RecipeImport,
ShareLink, ShoppingList, ShoppingListEntry,
ShoppingListRecipe, Step, Storage, Sync, SyncLog,
Unit, UserPreference, ViewLog, SupermarketCategory,
Supermarket, SupermarketCategoryRelation, ImportLog, BookmarkletImport)
Unit, UserPreference, ViewLog, SupermarketCategory, Supermarket,
SupermarketCategoryRelation, ImportLog, BookmarkletImport)
from cookbook.templatetags.custom_tags import markdown
@ -488,6 +488,7 @@ class BookmarkletImportSerializer(serializers.ModelSerializer):
fields = ('id', 'url', 'html', 'created_by', 'created_at')
read_only_fields = ('created_by', 'space')
# Export/Import Serializers
class KeywordExportSerializer(KeywordSerializer):

View File

@ -1,385 +0,0 @@
<!--I PROBLABLY DON'T NEED THIS??-->
{% extends "base.html" %}
{% load crispy_forms_filters %}
{% load i18n %}
{% load static %}
{% block title %}{% trans 'Import Recipe' %}{% endblock %}
{% block extra_head %}
{% include 'include/vue_base.html' %}
<script src="{% static 'js/vue-multiselect.min.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/vue-multiselect.min.css' %}">
<script src="{% static 'js/vue-jstree.js' %}"></script>
<style>
.tree-anchor {
width:95%;
}
</style>
{% endblock %}
{% block content %}
<div id="app">
<div v-if="!parsed">
<h2>{% trans 'Import From Source' %}</h2>
<div class="input-group input-group-lg">
<textarea class="form-control" v-model="html_recipe" rows="12" style="font-size: 12px"></textarea>
</div>
<small class="text-muted">Simply paste a web page source or JSON document into this textarea and click import.</small>
<br>
<button @click="loadRecipe()" class="btn btn-success" type="button"
id="id_btn_import"><i class="fas fa-code"></i>{% trans 'Import' %}
</button>
</div>
<br/>
<div v-if="loading" class="text-center">
<br/>
<i class="fas fa-spinner fa-spin fa-8x"></i>
</div>
<template v-if="recipe_data !== undefined">
<p>this is the recipe form
</template>
<div v-if="recipe_tree !== undefined" id="manage_tree">
<v-jstree :data="recipe_tree"
whole-row
collapse:true
draggable
@item-click="itemClick"
@item-drag-start="itemDragStart"
@item-drag-end="itemDragEnd"
@item-drop-before = "itemDropBefore"
@item-drop="itemDrop">
<template scope="_">
<div class="container-fluid" >
<div @click.ctrl="customItemClickWithCtrl">
<div class="row clearfix" style="width:80%; cursor: grab;" >
<i :class="_.vm.themeIconClasses" role="presentation" v-if="!_.model.loading"></i>
{% verbatim %}
[[_.model.name]]
{% endverbatim %}
<button style="border: 0px; background-color: transparent; cursor: pointer;"
@click="deleteNode(_.vm, _.model, $event)"><i class="fa fa-remove"></i></button>
</div>
</div>
</div>
</div>
</template>
</v-jstree>
</div>
<template v-if="error !== undefined">
<p>something terrible happened
</template>
</div>
{% endblock %}
{% block script %}
<script src="{% url 'javascript-catalog' %}"></script>
<script type="application/javascript">
let csrftoken = Cookies.get('csrftoken');
Vue.http.headers.common['X-CSRFToken'] = csrftoken;
//Vue.component('vue-multiselect', window.VueMultiselect.default)
let app = new Vue({
components: {
Multiselect: window.VueMultiselect.default
},
delimiters: ['[[', ']]'],
el: '#app',
data: {
html_recipe: '',
keywords: [],
keywords_loading: false,
units: [],
units_loading: false,
ingredients: [],
ingredients_loading: false,
recipe_data: undefined,
recipe_tree: [],
error: undefined,
loading: false,
all_keywords: false,
importing_recipe: false,
parsed: false,
searchText: '',
editingItem: {},
editingNode: null,
itemEvents: {
mouseover: function () {
console.log('mouseover')
},
contextmenu: function () {
console.log(arguments[2])
arguments[2].preventDefault()
console.log('contextmenu')
}
},
},
directives: {
tabindex: {
inserted(el) {
el.setAttribute('tabindex', 0);
}
}
},
mounted: function () {
this.searchKeywords('')
this.searchUnits('')
this.searchIngredients('')
},
methods: {
makeToast: function (title, message, variant = null) {
//TODO remove duplicate function in favor of central one
this.$bvToast.toast(message, {
title: title,
variant: variant,
toaster: 'b-toaster-top-center',
solid: true
})
},
loadRecipe: function () {
this.recipe_data = undefined
this.recipe_tree = undefined
this.error = undefined
this.parsed = true
this.loading = true
this.$http.post("{% url 'api_manual_recipe_from_json' %}", {'html_text': this.html_recipe}, {emulateJSON: true}).then((response) => {
console.log(response.data)
this.recipe_data = response.data['recipe_data'];
this.recipe_tree = response.data['recipe_tree'];
this.loading = false
}).catch((err) => {
this.error = err.data
this.loading = false
this.parsed = false
console.log(err)
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
})
},
importRecipe: function () {
if (this.importing_recipe) {
this.makeToast(gettext('Error'), gettext('Already importing the selected recipe, please wait!'), 'danger')
return;
}
this.importing_recipe = true
this.$set(this.recipe_data, 'all_keywords', this.all_keywords)
this.$http.post(`{% url 'data_import_url' %}`, this.recipe_data).then((response) => {
window.location.href = response.data
}).catch((err) => {
console.log(err);
this.makeToast(gettext('Error'), gettext('An error occurred while trying to import this recipe!') + err.bodyText, 'danger')
})
},
deleteIngredient: function (i) {
this.recipe_data.recipeIngredient = this.recipe_data.recipeIngredient.filter(item => item !== i)
},
addIngredient: function (i) {
this.recipe_data.recipeIngredient.push({
unit: {id: Math.random() * 1000, text: '{{ request.user.userpreference.default_unit }}'},
amount: 0,
ingredient: {id: Math.random() * 1000, text: ''}
})
},
addIngredientType: function (tag, index) {
index = index.replace('ingredient_', '')
let new_ingredient = this.recipe_data.recipeIngredient[index]
new_ingredient.ingredient = {'id': Math.random() * 1000, 'text': tag}
this.ingredients.push(new_ingredient.ingredient)
this.recipe_data.recipeIngredient[index] = new_ingredient
},
addUnitType: function (tag, index) {
index = index.replace('unit_', '')
let new_unit = this.recipe_data.recipeIngredient[index]
new_unit.unit = {'id': Math.random() * 1000, 'text': tag}
this.units.push(new_unit.unit)
this.recipe_data.recipeIngredient[index] = new_unit
},
addKeyword: function (tag) {
let new_keyword = {'text':tag,'id':null}
this.recipe_data.keywords.push(new_keyword)
},
openUnitSelect: function (id) {
let index = id.replace('unit_', '')
if (this.recipe_data.recipeIngredient[index].unit !== null) {
this.$set(app.$refs.unit[index].$data, 'search', this.recipe_data.recipeIngredient[index].unit.text)
}
},
openIngredientSelect: function (id) {
let index = id.replace('ingredient_', '')
this.$set(this.$refs.ingredient[index].$data, 'search', this.recipe_data.recipeIngredient[index].ingredient.text)
},
searchKeywords: function (query) {
this.keywords_loading = true
this.$http.get("{% url 'dal_keyword' %}" + '?q=' + query).then((response) => {
this.keywords = response.data.results;
this.keywords_loading = false
}).catch((err) => {
console.log(err)
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
})
},
searchUnits: function (query) {
this.units_loading = true
this.$http.get("{% url 'dal_unit' %}" + '?q=' + query).then((response) => {
this.units = response.data.results;
if (this.recipe_data !== undefined) {
for (let x of Array.from(this.recipe_data.recipeIngredient)) {
if (x.unit !== null && x.unit.text !== '') {
this.units = this.units.filter(item => item.text !== x.unit.text)
this.units.push(x.unit)
}
}
}
this.units_loading = false
}).catch((err) => {
console.log(err)
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
})
},
searchIngredients: function (query) {
this.ingredients_loading = true
this.$http.get("{% url 'dal_food' %}" + '?q=' + query).then((response) => {
this.ingredients = response.data.results
if (this.recipe_data !== undefined) {
for (let x of Array.from(this.recipe_data.recipeIngredient)) {
if (x.ingredient.text !== '') {
this.ingredients = this.ingredients.filter(item => item.text !== x.ingredient.text)
this.ingredients.push(x.ingredient)
}
}
}
this.ingredients_loading = false
}).catch((err) => {
console.log(err)
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
})
},
itemClick (node) {
this.editingNode = node
this.editingItem = node.model
console.log(node.model.text + ' clicked !')
},
itemDragStart (node) {
console.log(node.model.text + ' drag start !')
},
itemDragEnd (node) {
console.log(node.model.text + ' drag end !')
},
itemDropBefore (node, item, draggedItem , e) {
if (!draggedItem) {
item.addChild({
text: "newNode",
value: "newNode"
})
}
},
itemDrop (node, item, draggedItem , e) {
var sortBy = function(attr,rev) {
if (rev == undefined) {
rev = 1;
} else {
rev = (rev) ? 1 : -1;
}
return function (a, b) {
a = a[attr];
b = b[attr];
if (a < b) {
return rev * -1;
}
if (a > b) {
return rev * 1;
}
return 0;
}
}
item.children.sort(sortBy('text', true))
this.$refs.tree.handleRecursionNodeChildren(draggedItem, function (childrenItem) {
childrenItem.selected = item.selected
})
console.log(node.model.text + ' drop !')
},
inputKeyUp: function () {
var text = this.searchText
const patt = new RegExp(text);
this.$refs.tree.handleRecursionNodeChilds(this.$refs.tree, function (node) {
if (text !== '' && node.model !== undefined) {
const str = node.model.text
if (patt.test(str)) {
node.$el.querySelector('.tree-anchor').style.color = 'red'
} else {
node.$el.querySelector('.tree-anchor').style.color = '#000'
} // or other operations
} else {
node.$el.querySelector('.tree-anchor').style.color = '#000'
}
})
},
addChildNode: function () {
if (this.editingItem.id !== undefined) {
this.editingItem.addChild({
text: "newNode",
value: "newNode"
})
}
},
removeNode: function () {
if (this.editingItem.id !== undefined) {
var index = this.editingNode.parentItem.indexOf(this.editingItem)
this.editingNode.parentItem.splice(index, 1)
}
},
addBeforeNode: function () {
if (this.editingItem.id !== undefined) {
this.editingItem.addBefore({
text: "newNode",
value: "newNode"
}, this.editingNode)
}
},
addAfterNode: function () {
if (this.editingItem.id !== undefined) {
this.editingItem.addAfter({
text: "newNode",
value: "newNode"
}, this.editingNode)
}
},
openChildren: function () {
if (this.editingItem.id !== undefined) {
this.editingItem.openChildren()
}
},
closeChildren: function () {
if (this.editingItem.id !== undefined) {
this.editingItem.closeChildren()
}
},
refreshNode: function () {
this.asyncData = [
this.$refs.tree2.initializeLoading()
]
this.$refs.tree2.handleAsyncLoad(this.asyncData, this.$refs.tree2)
},
customItemClick: function (node ,item, e) {
e.stopPropagation()
var index = node.parentItem.indexOf(item)
node.parentItem.splice(index, 1)
},
customItemClickWithCtrl: function () {
console.log('click + ctrl')
}
}
});
</script>
{% endblock %}

View File

@ -1,7 +1,6 @@
{% extends "base.html" %}
{% load i18n %}
{% load static %}
{% load custom_tags %}
{% block title %}{% trans 'URL Import' %}{% endblock %}
@ -21,15 +20,9 @@
{% endblock %}
{% block content %}
<div id="app">
<div class="row px-3" style="justify-content:space-between;">
<h2> {% trans 'Import' %} </h2>
<a class="btn btn-outline-info btn-sm"
style="height:50%"
href="{% bookmarklet request %}"
title="{% trans 'Drag me to your bookmarks to import recipes from anywhere' %}">
<img src="{% static 'assets/favicon-16x16.png' %}">{% trans 'Bookmark Me!' %} </a>
</div>
<nav class="nav nav-pills flex-sm-row" style="margin-bottom:10px">
<a class="nav-link active" href="#nav-url" data-toggle="tab" role="tab" aria-controls="nav-url" aria-selected="true" @click="mode='url'">URL</a>
<a class="nav-link" href="#nav-app" data-toggle="tab" role="tab" aria-controls="nav-app" @click="mode='app'">App</a>
@ -605,6 +598,7 @@
<script type="application/javascript">
let csrftoken = Cookies.get('csrftoken');
Vue.http.headers.common['X-CSRFToken'] = csrftoken;
Vue.component('vue-multiselect', window.VueMultiselect.default)
let app = new Vue({
@ -625,7 +619,7 @@
recipe_data: undefined,
error: undefined,
loading: false,
preview: undefined,
preview: false,
preview_type: 'json',
all_keywords: false,
importing_recipe: false,
@ -729,6 +723,35 @@
this.makeToast(gettext('Error'), gettext('There was an error deleting bookmarklet!') + err.bodyText, 'danger')
})
},
loadBookmarklet: function(id_bkmk) {
let uri = window.location.search.substring(1);
let params = new URLSearchParams(uri);
q = params.get("id")
console.log(q)
this.error = undefined
this.loading = true
this.$http.get("{% url 'api:bookmarkletimport-list' %}?id=" + id_bkmk ).then((response) => {
console.log(response.data)
this.automatic = false
this.source_data = response.data[0]['html']
this.remote_url = response.data[0]['url']
this.loadRecipe()
}).catch((err) => {
this.error = err.data
this.loading = false
console.log(err)
this.makeToast(gettext('Error'), gettext('There was an error loading bookmarklet!') + err.bodyText, 'danger')
})
},
deleteBookmarklet: function(id_bkmk) {
this.error = undefined
this.$http.delete("{% url 'api:bookmarkletimport-list' %}" + id_bkmk +"/").then((response) => {
}).catch((err) => {
this.error = err.data
console.log(err)
this.makeToast(gettext('Error'), gettext('There was an error deleting bookmarklet!') + err.bodyText, 'danger')
})
},
showRecipe: function() {
this.preview = false
this.recipe_data = this.recipe_json

View File

@ -1,5 +1,4 @@
import bleach
import re
import markdown as md
from bleach_allowlist import markdown_attrs, markdown_tags
from cookbook.helper.mdx_attributes import MarkdownFormatExtension
@ -7,10 +6,8 @@ from cookbook.helper.mdx_urlize import UrlizeExtension
from cookbook.models import Space, get_model_name
from django import template
from django.db.models import Avg
from django.templatetags.static import static
from django.urls import NoReverseMatch, reverse
from recipes import settings
from rest_framework.authtoken.models import Token
from gettext import gettext as _
register = template.Library()

View File

@ -76,8 +76,7 @@ def test_add(arg, request, u1_s2):
print(r.content)
assert r.status_code == arg[1]
if r.status_code == 201:
# changed to name - when multiple tests run DB isn't cleared between tests
assert response['name'] == 'test'
assert response['id'] == 1
r = c.get(reverse(DETAIL_URL, args={response['id']}))
assert r.status_code == 200
r = u1_s2.get(reverse(DETAIL_URL, args={response['id']}))

View File

@ -1705,7 +1705,7 @@ MADAME_DESSERT = {
"servings": 6,
"prepTime": 0,
"cookTime": 20,
"image": "https://madamedessert.de/wp-content/uploads/2020/02/Madame-Dessert_Schokopudding-Schokoladenpudding-mit-echter-Schokolade-0238-scaled.jpg",
"image": "https://assets.madamedessert.de/wp-content/uploads/2020/02/25163328/Madame-Dessert_Schokopudding-Schokoladenpudding-mit-echter-Schokolade-0238-scaled.jpg",
"keywords": [
{
"id": 7588432,
@ -1795,7 +1795,7 @@ MADAME_DESSERT = {
"original": "1 TL Vanilleextrakt"
},
{
"amount": 125,
"amount": 150,
"unit": {
"text": "g",
"id": 24254
@ -1805,7 +1805,7 @@ MADAME_DESSERT = {
"id": 42645
},
"note": "",
"original": "125 g Zucker"
"original": "150 g Zucker"
},
{
"amount": 30,

File diff suppressed because one or more lines are too long

View File

@ -3,8 +3,7 @@ import pytest
from django.urls import reverse
from ._recipes import (
ALLRECIPES, AMERICAS_TEST_KITCHEN, CHEF_KOCH, COOKPAD,
from ._recipes import (ALLRECIPES, AMERICAS_TEST_KITCHEN, CHEF_KOCH, COOKPAD,
COOKS_COUNTRY, DELISH, FOOD_NETWORK, GIALLOZAFFERANO, JOURNAL_DES_FEMMES,
MADAME_DESSERT, MARMITON, TASTE_OF_HOME, TUDOGOSTOSO)

View File

@ -16,7 +16,6 @@ from django.http import FileResponse, HttpResponse, JsonResponse, HttpResponseRe
from django.shortcuts import redirect, render, get_object_or_404
from django.urls import reverse
from django.utils.translation import gettext as _
from django.views.decorators.csrf import csrf_exempt
from icalendar import Calendar, Event
from rest_framework import decorators, viewsets
@ -36,7 +35,7 @@ from cookbook.helper.ingredient_parser import parse
from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsGuest,
CustomIsOwner, CustomIsShare,
CustomIsShared, CustomIsUser,
group_required)
group_required, share_link_valid)
from cookbook.helper.recipe_html_import import get_recipe_from_source
from cookbook.helper.recipe_url_import import get_from_scraper
from cookbook.models import (CookLog, Food, Ingredient, Keyword, MealPlan,
@ -58,9 +57,9 @@ from cookbook.serializer import (FoodSerializer, IngredientSerializer,
StorageSerializer, SyncLogSerializer,
SyncSerializer, UnitSerializer,
UserNameSerializer, UserPreferenceSerializer,
ViewLogSerializer, CookLogSerializer,
RecipeBookEntrySerializer, RecipeOverviewSerializer,
SupermarketSerializer, ImportLogSerializer, BookmarkletImportSerializer)
ViewLogSerializer, CookLogSerializer, RecipeBookEntrySerializer,
RecipeOverviewSerializer, SupermarketSerializer, ImportLogSerializer,
BookmarkletImportSerializer)
from recipes.settings import DEMO

View File

@ -94,7 +94,9 @@ def batch_edit(request):
msg = ngettext(
'Batch edit done. %(count)d recipe was updated.',
'Batch edit done. %(count)d Recipes where updated.',
count) % {'count': count, }
count) % {
'count': count,
}
messages.add_message(request, messages.SUCCESS, msg)
return redirect('data_batch_edit')
@ -190,13 +192,12 @@ def import_url(request):
return HttpResponse(reverse('view_recipe', args=[recipe.pk]))
if 'id' in request.GET:
context = {'bookmarklet': 25}
context = {'bookmarklet': request.GET.get('id', '')}
else:
context = {}
return render(request, 'url_import.html', context)
class Object(object):
pass

0
manage.py Executable file → Normal file
View File