forked from wiktor/spejstore-new
Migrate old django to the newest version
Django 1.x is no longer supported, and the app needed migration to 4.x A lot of libraries has been unsupported or removed, so there's a few of unrelated changes, but necessary for the migration process to work.
This commit is contained in:
parent
659f04ce9c
commit
45ad9bf88c
|
@ -1,16 +1,34 @@
|
||||||
certifi==2017.4.17
|
asgiref==3.7.2
|
||||||
chardet==3.0.3
|
certifi==2023.5.7
|
||||||
Django==1.11.15
|
cffi==1.15.1
|
||||||
git+https://github.com/djangonauts/django-hstore@61427e474cb2f4be8fdfce225d78a5330bc77eb0#egg=django-hstore
|
chardet==5.1.0
|
||||||
git+https://github.com/d42/django-tree@687c01c02d91cada9ca1912e34e482da9e73e27a#egg=django-tree
|
charset-normalizer==3.2.0
|
||||||
django-appconf==1.0.2
|
colorclass==2.2.2
|
||||||
django-flat-responsive==2.0
|
cryptography==41.0.1
|
||||||
social-auth-app-django==2.1.0
|
defusedxml==0.7.1
|
||||||
Django-Select2==6.3.1
|
Django==3.2.20
|
||||||
djangorestframework==3.5.4
|
django-admin-hstore-widget==1.2.1
|
||||||
Pillow==3.3.1
|
django-appconf==1.0.5
|
||||||
psycopg2==2.7.5
|
django-hstore==1.4.2
|
||||||
djangorestframework-hstore==1.3
|
django-markdown2==0.3.1
|
||||||
requests==2.16.5
|
django-select2==8.1.2
|
||||||
urllib3==1.21.1
|
django-tree==0.5.6
|
||||||
django_markdown2==0.3.0
|
djangorestframework==3.14.0
|
||||||
|
docopt==0.6.2
|
||||||
|
idna==3.4
|
||||||
|
markdown2==2.4.9
|
||||||
|
oauthlib==3.2.2
|
||||||
|
packaging==23.1
|
||||||
|
Pillow==10.0.0
|
||||||
|
psycopg2==2.9.6
|
||||||
|
pycparser==2.21
|
||||||
|
PyJWT==2.7.0
|
||||||
|
python3-openid==3.2.0
|
||||||
|
pytz==2023.3
|
||||||
|
requests==2.31.0
|
||||||
|
requests-oauthlib==1.3.1
|
||||||
|
social-auth-app-django==5.2.0
|
||||||
|
social-auth-core==4.4.2
|
||||||
|
sqlparse==0.4.4
|
||||||
|
terminaltables==3.1.10
|
||||||
|
urllib3==2.0.3
|
||||||
|
|
|
@ -32,7 +32,9 @@ SECRET_KEY = env("SECRET_KEY", "#hjthi7_udsyt*9eeyb&nwgw5x=%pk_lnz3+u2tg9@=w3p1m
|
||||||
DEBUG = not PROD
|
DEBUG = not PROD
|
||||||
|
|
||||||
ALLOWED_HOSTS = env(
|
ALLOWED_HOSTS = env(
|
||||||
"ALLOWED_HOSTS", "devinventory,inventory.waw.hackerspace.pl,i,inventory"
|
"ALLOWED_HOSTS",
|
||||||
|
"devinventory,inventory.waw.hackerspace.pl,i,inventory"
|
||||||
|
+ (",127.0.0.1" if PROD != True else ""),
|
||||||
).split(",")
|
).split(",")
|
||||||
LOGIN_REDIRECT_URL = "/admin/"
|
LOGIN_REDIRECT_URL = "/admin/"
|
||||||
|
|
||||||
|
@ -40,7 +42,6 @@ LOGIN_REDIRECT_URL = "/admin/"
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
"flat_responsive",
|
|
||||||
"django.contrib.admin",
|
"django.contrib.admin",
|
||||||
"django.contrib.auth",
|
"django.contrib.auth",
|
||||||
"django.contrib.contenttypes",
|
"django.contrib.contenttypes",
|
||||||
|
@ -49,13 +50,13 @@ INSTALLED_APPS = [
|
||||||
"django.contrib.staticfiles",
|
"django.contrib.staticfiles",
|
||||||
"django.contrib.postgres",
|
"django.contrib.postgres",
|
||||||
"social_django",
|
"social_django",
|
||||||
"django_hstore",
|
|
||||||
"tree",
|
"tree",
|
||||||
"django_select2",
|
"django_select2",
|
||||||
"rest_framework",
|
"rest_framework",
|
||||||
"rest_framework.authtoken",
|
"rest_framework.authtoken",
|
||||||
"django_markdown2",
|
"django_markdown2",
|
||||||
"storage",
|
"storage",
|
||||||
|
"django_admin_hstore_widget",
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
@ -125,11 +126,6 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
# select2
|
|
||||||
|
|
||||||
SELECT2_JS = "js/select2.min.js"
|
|
||||||
SELECT2_CSS = "css/select2.min.css"
|
|
||||||
SELECT2_I18N_PATH = ""
|
|
||||||
|
|
||||||
AUTHENTICATION_BACKENDS = (
|
AUTHENTICATION_BACKENDS = (
|
||||||
"auth.backend.HSWawOAuth2",
|
"auth.backend.HSWawOAuth2",
|
||||||
|
|
|
@ -0,0 +1,216 @@
|
||||||
|
var initDjangoHStoreWidget = function (hstore_field_name, inline_prefix) {
|
||||||
|
// ignore inline templates
|
||||||
|
// if hstore_field_name contains "__prefix__"
|
||||||
|
if (hstore_field_name.indexOf("__prefix__") > -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var $ = django.jQuery;
|
||||||
|
|
||||||
|
// processing inlines
|
||||||
|
if (hstore_field_name.indexOf("inline") > -1) {
|
||||||
|
var inlineClass = $("#id_" + hstore_field_name)
|
||||||
|
.parents(".inline-related, .grp-group")
|
||||||
|
.attr("class");
|
||||||
|
// if using TabularInlines stop here
|
||||||
|
// TabularInlines not supported
|
||||||
|
if (inlineClass.indexOf("tabular") > -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reusable function that retrieves a template even if ID is not correct
|
||||||
|
// (written to support inlines)
|
||||||
|
var retrieveTemplate = function (template_name, field_name) {
|
||||||
|
var specific_template = $("#" + template_name + "-" + field_name);
|
||||||
|
// if found specific template return that
|
||||||
|
if (specific_template.length) {
|
||||||
|
return specific_template.html();
|
||||||
|
} else {
|
||||||
|
// get fallback template
|
||||||
|
var html = $("." + template_name + "-inline").html();
|
||||||
|
// replace all occurrences of __prefix__ with field_name
|
||||||
|
// and return
|
||||||
|
html = html.replace(/__prefix__/g, inline_prefix);
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// reusable function that compiles the UI
|
||||||
|
var compileUI = function (params) {
|
||||||
|
var hstore_field_id = "id_" + hstore_field_name,
|
||||||
|
original_textarea = $("#" + hstore_field_id),
|
||||||
|
original_value = original_textarea.val(),
|
||||||
|
original_container = original_textarea
|
||||||
|
.parents(".form-row, .grp-row")
|
||||||
|
.eq(0),
|
||||||
|
errorHtml = original_container.find(".errorlist").html(),
|
||||||
|
json_data = {};
|
||||||
|
|
||||||
|
if (original_value !== "") {
|
||||||
|
// manage case in which textarea is blank
|
||||||
|
try {
|
||||||
|
json_data = JSON.parse(original_value);
|
||||||
|
} catch (e) {
|
||||||
|
alert("invalid JSON:\n" + e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var hstore_field_data = {
|
||||||
|
id: hstore_field_id,
|
||||||
|
label: original_container.find("label").text(),
|
||||||
|
name: hstore_field_name,
|
||||||
|
value: original_textarea.val(),
|
||||||
|
help: original_container.find(".grp-help, .help").text(),
|
||||||
|
errors: errorHtml,
|
||||||
|
data: json_data,
|
||||||
|
},
|
||||||
|
// compile template
|
||||||
|
ui_html = retrieveTemplate("hstore-ui-template", hstore_field_name),
|
||||||
|
compiled_ui_html = _.template(ui_html, hstore_field_data);
|
||||||
|
|
||||||
|
// this is just to DRY up a bit
|
||||||
|
if (params && params.replace_original === true) {
|
||||||
|
// remove original textarea to avoid having two textareas with same ID
|
||||||
|
original_textarea.remove();
|
||||||
|
// inject compiled template and hide original
|
||||||
|
original_container.after(compiled_ui_html).hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
return compiled_ui_html;
|
||||||
|
};
|
||||||
|
|
||||||
|
// generate UI
|
||||||
|
compileUI({ replace_original: true });
|
||||||
|
|
||||||
|
// cache other objects that we'll reuse
|
||||||
|
var row_html = retrieveTemplate("hstore-row-template", hstore_field_name),
|
||||||
|
empty_row = _.template(row_html, { key: "", value: "" }),
|
||||||
|
$hstore = $("#id_" + hstore_field_name).parents(".hstore");
|
||||||
|
|
||||||
|
// reusable function that updates the textarea value
|
||||||
|
var updateTextarea = function (container) {
|
||||||
|
// init empty json object
|
||||||
|
var new_value = {},
|
||||||
|
raw_textarea = container.find("textarea"),
|
||||||
|
rows = container.find(".form-row, .grp-row");
|
||||||
|
|
||||||
|
// loop over each object and populate json
|
||||||
|
rows.each(function () {
|
||||||
|
var inputs = $(this).find("input"),
|
||||||
|
key = $(this).find(".hs-key").val(),
|
||||||
|
value = $(this).find(".hs-val").val();
|
||||||
|
new_value[key] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
// update textarea value
|
||||||
|
$(raw_textarea).val(JSON.stringify(new_value, null, 4));
|
||||||
|
};
|
||||||
|
|
||||||
|
// remove row link
|
||||||
|
$hstore.delegate("a.remove-row", "click", function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
// cache container jquery object before $(this) gets removed
|
||||||
|
$(this).parents(".form-row, .grp-row").eq(0).remove();
|
||||||
|
updateTextarea($hstore);
|
||||||
|
});
|
||||||
|
|
||||||
|
// add row link
|
||||||
|
$hstore.delegate("a.hs-add-row, .hs-add-row a", "click", function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$hstore.find(".hstore-rows").append(empty_row);
|
||||||
|
$(".django-select2").djangoSelect2();
|
||||||
|
$("select").on("select2:close", function () {
|
||||||
|
$(this).focus();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// toggle textarea link
|
||||||
|
$hstore.delegate(".hstore-toggle-txtarea", "click", function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var raw_textarea = $hstore.find(".hstore-textarea"),
|
||||||
|
hstore_rows = $hstore.find(".hstore-rows"),
|
||||||
|
add_row = $hstore.find(".hs-add-row");
|
||||||
|
|
||||||
|
if (raw_textarea.is(":visible")) {
|
||||||
|
var compiled_ui = compileUI();
|
||||||
|
|
||||||
|
// in case of JSON error
|
||||||
|
if (compiled_ui === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jquery < 1.8
|
||||||
|
try {
|
||||||
|
var $ui = $(compiled_ui);
|
||||||
|
} catch (e) {
|
||||||
|
// jquery >= 1.8
|
||||||
|
var $ui = $($.parseHTML(compiled_ui));
|
||||||
|
}
|
||||||
|
|
||||||
|
// update rows with only relevant content
|
||||||
|
hstore_rows.html($ui.find(".hstore-rows").html());
|
||||||
|
|
||||||
|
raw_textarea.hide();
|
||||||
|
hstore_rows.show();
|
||||||
|
add_row.show();
|
||||||
|
|
||||||
|
$(".django-select2").djangoSelect2();
|
||||||
|
} else {
|
||||||
|
raw_textarea.show();
|
||||||
|
hstore_rows.hide();
|
||||||
|
add_row.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// update textarea whenever a field changes
|
||||||
|
$hstore.delegate(".hs-val", "keyup propertychange", function () {
|
||||||
|
updateTextarea($hstore);
|
||||||
|
});
|
||||||
|
|
||||||
|
$hstore.delegate(".hs-key", "change", function () {
|
||||||
|
updateTextarea($hstore);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
window.addEventListener("load", function () {
|
||||||
|
// support inlines
|
||||||
|
// bind only once
|
||||||
|
if (django.hstoreWidgetBoundInlines === undefined) {
|
||||||
|
var $ = django.jQuery;
|
||||||
|
$(
|
||||||
|
".grp-group .grp-add-handler, .inline-group .hs-add-row a, .inline-group .add-row"
|
||||||
|
).click(function (e) {
|
||||||
|
var hstore_original_textareas = $(this)
|
||||||
|
.parents(".grp-group, .inline-group")
|
||||||
|
.eq(0)
|
||||||
|
.find(".hstore-original-textarea");
|
||||||
|
// if module contains .hstore-original-textarea
|
||||||
|
if (hstore_original_textareas.length > 0) {
|
||||||
|
// loop over each inline
|
||||||
|
$(this)
|
||||||
|
.parents(".grp-group, .inline-group")
|
||||||
|
.find(".grp-items div.grp-dynamic-form, .inline-related")
|
||||||
|
.each(function (e, i) {
|
||||||
|
var prefix = i;
|
||||||
|
// loop each textarea
|
||||||
|
$(this)
|
||||||
|
.find(".hstore-original-textarea")
|
||||||
|
.each(function (e, i) {
|
||||||
|
// cache field name
|
||||||
|
var field_name = $(this).attr("name");
|
||||||
|
// ignore templates
|
||||||
|
// if name attribute contains __prefix__
|
||||||
|
if (field_name.indexOf("prefix") > -1) {
|
||||||
|
// skip to next
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
initDjangoHStoreWidget(field_name, prefix);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
django.hstoreWidgetBoundInlines = true;
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,208 +0,0 @@
|
||||||
var initDjangoHStoreWidget = function(hstore_field_name, inline_prefix) {
|
|
||||||
// ignore inline templates
|
|
||||||
// if hstore_field_name contains "__prefix__"
|
|
||||||
if(hstore_field_name.indexOf('__prefix__') > -1){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// processing inlines
|
|
||||||
if(hstore_field_name.indexOf('inline') > -1){
|
|
||||||
var inlineClass = $('#id_'+hstore_field_name).parents('.inline-related, .grp-group').attr('class');
|
|
||||||
// if using TabularInlines stop here
|
|
||||||
// TabularInlines not supported
|
|
||||||
if (inlineClass.indexOf('tabular') > -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// reusable function that retrieves a template even if ID is not correct
|
|
||||||
// (written to support inlines)
|
|
||||||
var retrieveTemplate = function(template_name, field_name){
|
|
||||||
var specific_template = $('#'+template_name+'-'+field_name);
|
|
||||||
// if found specific template return that
|
|
||||||
if(specific_template.length){
|
|
||||||
return specific_template.html();
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
// get fallback template
|
|
||||||
var html = $('.'+template_name+'-inline').html();
|
|
||||||
// replace all occurrences of __prefix__ with field_name
|
|
||||||
// and return
|
|
||||||
html = html.replace(/__prefix__/g, inline_prefix);
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// reusable function that compiles the UI
|
|
||||||
var compileUI = function(params){
|
|
||||||
var hstore_field_id = 'id_'+hstore_field_name,
|
|
||||||
original_textarea = $('#'+hstore_field_id),
|
|
||||||
original_value = original_textarea.val(),
|
|
||||||
original_container = original_textarea.parents('.form-row, .grp-row').eq(0),
|
|
||||||
errorHtml = original_container.find('.errorlist').html(),
|
|
||||||
json_data = {};
|
|
||||||
|
|
||||||
if(original_value !== ''){
|
|
||||||
// manage case in which textarea is blank
|
|
||||||
try{
|
|
||||||
json_data = JSON.parse(original_value);
|
|
||||||
}
|
|
||||||
catch(e){
|
|
||||||
alert('invalid JSON:\n'+e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var hstore_field_data = {
|
|
||||||
"id": hstore_field_id,
|
|
||||||
"label": original_container.find('label').text(),
|
|
||||||
"name": hstore_field_name,
|
|
||||||
"value": original_textarea.val(),
|
|
||||||
"help": original_container.find('.grp-help, .help').text(),
|
|
||||||
"errors": errorHtml,
|
|
||||||
"data": json_data
|
|
||||||
},
|
|
||||||
// compile template
|
|
||||||
ui_html = retrieveTemplate('hstore-ui-template', hstore_field_name),
|
|
||||||
compiled_ui_html = _.template(ui_html, hstore_field_data);
|
|
||||||
|
|
||||||
// this is just to DRY up a bit
|
|
||||||
if(params && params.replace_original === true){
|
|
||||||
// remove original textarea to avoid having two textareas with same ID
|
|
||||||
original_textarea.remove();
|
|
||||||
// inject compiled template and hide original
|
|
||||||
original_container.after(compiled_ui_html).hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
return compiled_ui_html;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// generate UI
|
|
||||||
compileUI({ replace_original: true });
|
|
||||||
|
|
||||||
// cache other objects that we'll reuse
|
|
||||||
var row_html = retrieveTemplate('hstore-row-template', hstore_field_name),
|
|
||||||
empty_row = _.template(row_html, { 'key': '', 'value': '' }),
|
|
||||||
$hstore = $('#id_'+hstore_field_name).parents('.hstore');
|
|
||||||
|
|
||||||
// reusable function that updates the textarea value
|
|
||||||
var updateTextarea = function(container) {
|
|
||||||
// init empty json object
|
|
||||||
var new_value = {},
|
|
||||||
raw_textarea = container.find('textarea'),
|
|
||||||
rows = container.find('.form-row, .grp-row');
|
|
||||||
|
|
||||||
// loop over each object and populate json
|
|
||||||
rows.each(function() {
|
|
||||||
var inputs = $(this).find('input'),
|
|
||||||
key = $(this).find('.hs-key').val(),
|
|
||||||
value = $(this).find('.hs-val').val();
|
|
||||||
new_value[key] = value;
|
|
||||||
});
|
|
||||||
|
|
||||||
// update textarea value
|
|
||||||
$(raw_textarea).val(JSON.stringify(new_value, null, 4));
|
|
||||||
};
|
|
||||||
|
|
||||||
// remove row link
|
|
||||||
$hstore.delegate('a.remove-row', 'click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
// cache container jquery object before $(this) gets removed
|
|
||||||
$(this).parents('.form-row, .grp-row').eq(0).remove();
|
|
||||||
updateTextarea($hstore);
|
|
||||||
});
|
|
||||||
|
|
||||||
// add row link
|
|
||||||
$hstore.delegate('a.hs-add-row, .hs-add-row a', 'click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
$hstore.find('.hstore-rows').append(empty_row);
|
|
||||||
$('.django-select2').djangoSelect2()
|
|
||||||
$('select').on( 'select2:close', function () {
|
|
||||||
$(this).focus();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// toggle textarea link
|
|
||||||
$hstore.delegate('.hstore-toggle-txtarea', 'click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
var raw_textarea = $hstore.find('.hstore-textarea'),
|
|
||||||
hstore_rows = $hstore.find('.hstore-rows'),
|
|
||||||
add_row = $hstore.find('.hs-add-row');
|
|
||||||
|
|
||||||
if(raw_textarea.is(':visible')) {
|
|
||||||
|
|
||||||
var compiled_ui = compileUI();
|
|
||||||
|
|
||||||
// in case of JSON error
|
|
||||||
if(compiled_ui === false){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// jquery < 1.8
|
|
||||||
try{
|
|
||||||
var $ui = $(compiled_ui);
|
|
||||||
}
|
|
||||||
// jquery >= 1.8
|
|
||||||
catch(e){
|
|
||||||
var $ui = $($.parseHTML(compiled_ui));
|
|
||||||
}
|
|
||||||
|
|
||||||
// update rows with only relevant content
|
|
||||||
hstore_rows.html($ui.find('.hstore-rows').html());
|
|
||||||
|
|
||||||
raw_textarea.hide();
|
|
||||||
hstore_rows.show();
|
|
||||||
add_row.show();
|
|
||||||
|
|
||||||
$('.django-select2').djangoSelect2()
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
raw_textarea.show();
|
|
||||||
hstore_rows.hide();
|
|
||||||
add_row.hide();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// update textarea whenever a field changes
|
|
||||||
$hstore.delegate('.hs-val', 'keyup propertychange', function() {
|
|
||||||
updateTextarea($hstore);
|
|
||||||
});
|
|
||||||
|
|
||||||
$hstore.delegate('.hs-key', 'change', function() {
|
|
||||||
updateTextarea($hstore);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
django.jQuery(window).load(function() {
|
|
||||||
// support inlines
|
|
||||||
// bind only once
|
|
||||||
if(django.hstoreWidgetBoundInlines === undefined){
|
|
||||||
var $ = django.jQuery;
|
|
||||||
$('.grp-group .grp-add-handler, .inline-group .hs-add-row a, .inline-group .add-row').click(function(e){
|
|
||||||
var hstore_original_textareas = $(this).parents('.grp-group, .inline-group').eq(0).find('.hstore-original-textarea');
|
|
||||||
// if module contains .hstore-original-textarea
|
|
||||||
if(hstore_original_textareas.length > 0){
|
|
||||||
// loop over each inline
|
|
||||||
$(this).parents('.grp-group, .inline-group').find('.grp-items div.grp-dynamic-form, .inline-related').each(function(e, i){
|
|
||||||
var prefix = i;
|
|
||||||
// loop each textarea
|
|
||||||
$(this).find('.hstore-original-textarea').each(function(e, i){
|
|
||||||
// cache field name
|
|
||||||
var field_name = $(this).attr('name');
|
|
||||||
// ignore templates
|
|
||||||
// if name attribute contains __prefix__
|
|
||||||
if(field_name.indexOf('prefix') > -1){
|
|
||||||
// skip to next
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
initDjangoHStoreWidget(field_name, prefix);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
django.hstoreWidgetBoundInlines = true;
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,16 +1,17 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from django_select2.forms import ModelSelect2Widget, Select2MultipleWidget
|
from django_select2.forms import Select2MultipleWidget
|
||||||
|
|
||||||
|
|
||||||
from .models import Item, ItemImage, Category, Label
|
from .models import Item, ItemImage, Category, Label
|
||||||
from .widgets import ItemSelectWidget, PropsSelectWidget
|
|
||||||
|
from .widgets import PropsSelectWidget
|
||||||
|
|
||||||
|
|
||||||
class ModelAdminMixin(object):
|
class ModelAdminMixin(object):
|
||||||
def has_add_permission(self, request, obj=None):
|
def has_add_permission(self, request, obj=None):
|
||||||
return request.user.is_authenticated()
|
return request.user.is_authenticated
|
||||||
|
|
||||||
has_change_permission = has_add_permission
|
has_change_permission = has_add_permission
|
||||||
has_delete_permission = has_add_permission
|
has_delete_permission = has_add_permission
|
||||||
|
@ -24,7 +25,6 @@ class ItemForm(forms.ModelForm):
|
||||||
model = Item
|
model = Item
|
||||||
exclude = []
|
exclude = []
|
||||||
widgets = {
|
widgets = {
|
||||||
"parent": ItemSelectWidget(model=Item),
|
|
||||||
"categories": Select2MultipleWidget,
|
"categories": Select2MultipleWidget,
|
||||||
"props": PropsSelectWidget,
|
"props": PropsSelectWidget,
|
||||||
}
|
}
|
||||||
|
@ -45,9 +45,11 @@ class ItemAdmin(ModelAdminMixin, admin.ModelAdmin):
|
||||||
form = ItemForm
|
form = ItemForm
|
||||||
inlines = [ItemImageInline, LabelInline]
|
inlines = [ItemImageInline, LabelInline]
|
||||||
save_on_top = True
|
save_on_top = True
|
||||||
|
autocomplete_fields = ["parent"]
|
||||||
|
search_fields = ["parent"]
|
||||||
|
|
||||||
def _name(self, obj):
|
def _name(self, obj):
|
||||||
return "-" * obj.get_level() + "> " + obj.name
|
return ("-" * (obj.get_level() or 0)) + "> " + obj.name
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
def save_model(self, request, obj, form, change):
|
||||||
super(ItemAdmin, self).save_model(request, obj, form, change)
|
super(ItemAdmin, self).save_model(request, obj, form, change)
|
||||||
|
@ -67,10 +69,6 @@ class ItemAdmin(ModelAdminMixin, admin.ModelAdmin):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
js = (
|
|
||||||
# Required by select2
|
|
||||||
"https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js",
|
|
||||||
)
|
|
||||||
css = {"all": ("css/admin.css",)}
|
css = {"all": ("css/admin.css",)}
|
||||||
|
|
||||||
def response_action(self, request, queryset):
|
def response_action(self, request, queryset):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from rest_framework import viewsets, generics, filters
|
from rest_framework import viewsets, generics, filters
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.decorators import detail_route
|
from rest_framework.decorators import action
|
||||||
|
|
||||||
from rest_framework.permissions import AllowAny
|
from rest_framework.permissions import AllowAny
|
||||||
|
|
||||||
from storage.models import Item, Label
|
from storage.models import Item, Label
|
||||||
|
@ -32,7 +33,7 @@ class LabelViewSet(viewsets.ModelViewSet):
|
||||||
queryset = Label.objects
|
queryset = Label.objects
|
||||||
serializer_class = LabelSerializer
|
serializer_class = LabelSerializer
|
||||||
|
|
||||||
@detail_route(methods=["post"], permission_classes=[AllowAny])
|
@action(detail=True, methods=["post"], permission_classes=[AllowAny])
|
||||||
def print(self, request, pk):
|
def print(self, request, pk):
|
||||||
quantity = min(int(request.query_params.get("quantity", 1)), 5)
|
quantity = min(int(request.query_params.get("quantity", 1)), 5)
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
|
@ -52,7 +53,7 @@ class ItemViewSet(viewsets.ModelViewSet):
|
||||||
ordering_fields = "__all__"
|
ordering_fields = "__all__"
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return Item.get_roots()
|
return Item.objects.filter_roots()
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
|
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
|
||||||
|
@ -73,7 +74,7 @@ class ItemViewSet(viewsets.ModelViewSet):
|
||||||
except Label.DoesNotExist:
|
except Label.DoesNotExist:
|
||||||
raise Http404()
|
raise Http404()
|
||||||
|
|
||||||
@detail_route(methods=["post"], permission_classes=[AllowAny])
|
@action(detail=True, methods=["post"], permission_classes=[AllowAny])
|
||||||
def print(self, request, pk):
|
def print(self, request, pk):
|
||||||
# todo: deduplicate
|
# todo: deduplicate
|
||||||
quantity = min(int(request.query_params.get("quantity", 1)), 5)
|
quantity = min(int(request.query_params.get("quantity", 1)), 5)
|
||||||
|
@ -82,28 +83,36 @@ class ItemViewSet(viewsets.ModelViewSet):
|
||||||
obj.print()
|
obj.print()
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
@detail_route()
|
@action(
|
||||||
|
detail=True,
|
||||||
|
)
|
||||||
def children(self, request, pk):
|
def children(self, request, pk):
|
||||||
item = self.get_object()
|
item = self.get_object()
|
||||||
return Response(
|
return Response(
|
||||||
self.serializer_class(item.get_children().all(), many=True).data
|
self.serializer_class(item.get_children().all(), many=True).data
|
||||||
)
|
)
|
||||||
|
|
||||||
@detail_route()
|
@action(
|
||||||
|
detail=True,
|
||||||
|
)
|
||||||
def ancestors(self, request, pk):
|
def ancestors(self, request, pk):
|
||||||
item = self.get_object()
|
item = self.get_object()
|
||||||
return Response(
|
return Response(
|
||||||
self.serializer_class(item.get_ancestors().all(), many=True).data
|
self.serializer_class(item.get_ancestors().all(), many=True).data
|
||||||
)
|
)
|
||||||
|
|
||||||
@detail_route()
|
@action(
|
||||||
|
detail=True,
|
||||||
|
)
|
||||||
def descendants(self, request, pk):
|
def descendants(self, request, pk):
|
||||||
item = self.get_object()
|
item = self.get_object()
|
||||||
return Response(
|
return Response(
|
||||||
self.serializer_class(item.get_descendants().all(), many=True).data
|
self.serializer_class(item.get_descendants().all(), many=True).data
|
||||||
)
|
)
|
||||||
|
|
||||||
@detail_route()
|
@action(
|
||||||
|
detail=True,
|
||||||
|
)
|
||||||
def siblings(self, request, pk):
|
def siblings(self, request, pk):
|
||||||
item = self.get_object()
|
item = self.get_object()
|
||||||
return Response(
|
return Response(
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django_hstore.fields
|
from django.contrib.postgres.fields import HStoreField
|
||||||
|
from django.contrib.postgres.operations import HStoreExtension
|
||||||
|
from django.contrib.postgres.operations import TrigramExtension
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
@ -12,6 +14,8 @@ class Migration(migrations.Migration):
|
||||||
dependencies = []
|
dependencies = []
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
HStoreExtension(),
|
||||||
|
TrigramExtension(),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="Item",
|
name="Item",
|
||||||
fields=[
|
fields=[
|
||||||
|
@ -26,7 +30,7 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
("name", models.TextField()),
|
("name", models.TextField()),
|
||||||
("description", models.TextField()),
|
("description", models.TextField()),
|
||||||
("props", django_hstore.fields.DictionaryField()),
|
("props", HStoreField()),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,7 +5,11 @@ from __future__ import unicode_literals
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import django_hstore.fields
|
from django.contrib.postgres.fields import HStoreField
|
||||||
|
from django.contrib.postgres.operations import HStoreExtension
|
||||||
|
from django.contrib.postgres.operations import TrigramExtension
|
||||||
|
|
||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,12 +32,14 @@ class Migration(migrations.Migration):
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
HStoreExtension(),
|
||||||
|
TrigramExtension(),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="Item",
|
name="Item",
|
||||||
fields=[
|
fields=[
|
||||||
("name", models.TextField()),
|
("name", models.TextField()),
|
||||||
("description", models.TextField(blank=True)),
|
("description", models.TextField(blank=True)),
|
||||||
("props", django_hstore.fields.DictionaryField()),
|
("props", HStoreField()),
|
||||||
(
|
(
|
||||||
"uuid",
|
"uuid",
|
||||||
models.UUIDField(
|
models.UUIDField(
|
||||||
|
|
|
@ -4,7 +4,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import django_hstore.fields
|
from django.contrib.postgres.fields import HStoreField
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
@ -35,7 +35,7 @@ class Migration(migrations.Migration):
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name="item",
|
model_name="item",
|
||||||
name="props",
|
name="props",
|
||||||
field=django_hstore.fields.DictionaryField(blank=True),
|
field=HStoreField(blank=True),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name="label",
|
model_name="label",
|
||||||
|
|
|
@ -12,6 +12,6 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
DeleteTreeTrigger("storage.Item"),
|
DeleteTreeTrigger("storage.Item"),
|
||||||
CreateTreeTrigger("storage.Item", order_by=("name",)),
|
CreateTreeTrigger("storage.Item"),
|
||||||
RebuildPaths("storage.Item"),
|
RebuildPaths("storage.Item"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Generated by Django 3.2.20 on 2023-07-10 17:21
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('storage', '0006_category_icon_id'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='category',
|
||||||
|
options={'ordering': ['name'], 'verbose_name_plural': 'categories'},
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='item',
|
||||||
|
name='state',
|
||||||
|
field=models.CharField(choices=[('present', 'Present'), ('taken', 'Taken'), ('broken', 'Broken'), ('missing', 'Missing'), ('depleted', 'Depleted')], default='present', max_length=31),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 3.2.20 on 2023-07-10 22:40
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
from django.contrib.postgres.operations import HStoreExtension
|
||||||
|
from django.contrib.postgres.operations import TrigramExtension
|
||||||
|
|
||||||
|
|
||||||
|
# This migration is necessary for current production purposes.
|
||||||
|
# Technically is a no-op if extensions are turned on already.
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("storage", "0007_auto_20230710_1721"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
HStoreExtension(),
|
||||||
|
TrigramExtension(),
|
||||||
|
]
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Generated by Django 3.2.20 on 2023-07-11 12:27
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
from django.contrib.postgres.operations import HStoreExtension
|
||||||
|
from django.contrib.postgres.operations import TrigramExtension
|
||||||
|
from tree.operations import (
|
||||||
|
DeleteTreeTrigger,
|
||||||
|
CreateTreeTrigger,
|
||||||
|
RebuildPaths,
|
||||||
|
)
|
||||||
|
from tree.fields import PathField
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("storage", "0008_force_extensions_via_django"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
DeleteTreeTrigger("storage.Item"),
|
||||||
|
migrations.RemoveField("Item", "path"),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="item",
|
||||||
|
name="path",
|
||||||
|
field=PathField(db_index=True, order_by=["name"], size=None),
|
||||||
|
),
|
||||||
|
CreateTreeTrigger("item"),
|
||||||
|
RebuildPaths("item"),
|
||||||
|
]
|
|
@ -6,9 +6,10 @@ import re
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django_hstore import hstore
|
|
||||||
from tree.fields import PathField
|
from tree.fields import PathField
|
||||||
from tree.models import TreeModelMixin
|
from tree.models import TreeModelMixin
|
||||||
|
from django.contrib.postgres.fields import HStoreField
|
||||||
|
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
@ -46,7 +47,7 @@ class Category(models.Model):
|
||||||
class Item(models.Model, TreeModelMixin):
|
class Item(models.Model, TreeModelMixin):
|
||||||
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||||
|
|
||||||
parent = models.ForeignKey("self", null=True, blank=True)
|
parent = models.ForeignKey("self", null=True, blank=True, on_delete=models.CASCADE)
|
||||||
path = PathField()
|
path = PathField()
|
||||||
|
|
||||||
name = models.TextField()
|
name = models.TextField()
|
||||||
|
@ -54,17 +55,25 @@ class Item(models.Model, TreeModelMixin):
|
||||||
description = models.TextField(blank=True, null=True)
|
description = models.TextField(blank=True, null=True)
|
||||||
state = models.CharField(max_length=31, choices=STATES, default=STATES[0][0])
|
state = models.CharField(max_length=31, choices=STATES, default=STATES[0][0])
|
||||||
categories = models.ManyToManyField(Category, blank=True)
|
categories = models.ManyToManyField(Category, blank=True)
|
||||||
owner = models.ForeignKey(User, null=True, blank=True, related_name="owned_items")
|
owner = models.ForeignKey(
|
||||||
|
User,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name="owned_items",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
)
|
||||||
|
|
||||||
taken_by = models.ForeignKey(
|
taken_by = models.ForeignKey(
|
||||||
User, null=True, blank=True, related_name="taken_items"
|
User,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name="taken_items",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
)
|
)
|
||||||
taken_on = models.DateTimeField(blank=True, null=True)
|
taken_on = models.DateTimeField(blank=True, null=True)
|
||||||
taken_until = models.DateTimeField(blank=True, null=True)
|
taken_until = models.DateTimeField(blank=True, null=True)
|
||||||
|
|
||||||
props = hstore.DictionaryField(blank=True)
|
props = HStoreField(blank=True)
|
||||||
|
|
||||||
objects = hstore.HStoreManager()
|
|
||||||
|
|
||||||
def short_id(self):
|
def short_id(self):
|
||||||
# let's just hope we never have 4 294 967 296 things :)
|
# let's just hope we never have 4 294 967 296 things :)
|
||||||
|
@ -105,7 +114,7 @@ class Item(models.Model, TreeModelMixin):
|
||||||
|
|
||||||
|
|
||||||
class ItemImage(models.Model):
|
class ItemImage(models.Model):
|
||||||
item = models.ForeignKey(Item, related_name="images")
|
item = models.ForeignKey(Item, related_name="images", on_delete=models.CASCADE)
|
||||||
image = models.ImageField()
|
image = models.ImageField()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -114,7 +123,7 @@ class ItemImage(models.Model):
|
||||||
|
|
||||||
class Label(models.Model):
|
class Label(models.Model):
|
||||||
id = models.CharField(max_length=64, primary_key=True)
|
id = models.CharField(max_length=64, primary_key=True)
|
||||||
item = models.ForeignKey(Item, related_name="labels")
|
item = models.ForeignKey(Item, related_name="labels", on_delete=models.CASCADE)
|
||||||
style = models.CharField(
|
style = models.CharField(
|
||||||
max_length=32,
|
max_length=32,
|
||||||
choices=(("basic_99012_v1", "Basic Dymo 89x36mm label"),),
|
choices=(("basic_99012_v1", "Basic Dymo 89x36mm label"),),
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from storage.models import Item, Label, Category
|
from storage.models import Item, Label, Category
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework_hstore.serializers import HStoreSerializer
|
|
||||||
|
|
||||||
|
|
||||||
class ItemSerializer(HStoreSerializer):
|
class ItemSerializer(serializers.HStoreField):
|
||||||
categories = serializers.SlugRelatedField(
|
categories = serializers.SlugRelatedField(
|
||||||
queryset=Category.objects, many=True, slug_field="name"
|
queryset=Category.objects, many=True, slug_field="name"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,33 +1,27 @@
|
||||||
{% extends "admin/change_form.html" %}
|
{% extends "admin/change_form.html" %} {% block submit_buttons_top %} {# We want
|
||||||
|
add another to be default submit action #}
|
||||||
{% block submit_buttons_top %}
|
|
||||||
{# We want add another to be default submit action #}
|
|
||||||
<input type="submit" value="Save" class="hidden" name="_addanother" />
|
<input type="submit" value="Save" class="hidden" name="_addanother" />
|
||||||
{{ block.super }}
|
{{ block.super }} {% endblock %} {% block submit_buttons_bottom %} {# We want
|
||||||
{% endblock %}
|
add another to be default submit action #}
|
||||||
|
|
||||||
{% block submit_buttons_bottom %}
|
|
||||||
{# We want add another to be default submit action #}
|
|
||||||
<input type="submit" value="Save" class="hidden" name="_addanother" />
|
<input type="submit" value="Save" class="hidden" name="_addanother" />
|
||||||
{{ block.super }}
|
{{ block.super }} {% endblock %} {% block content %}{{ block.super }}
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}{{ block.super }}
|
|
||||||
<script>
|
<script>
|
||||||
$(function() {
|
django.jQuery(function () {
|
||||||
function fmt(state) {
|
function fmt(state) {
|
||||||
if (!state.id) {
|
if (!state.id) {
|
||||||
return state.text;
|
return state.text;
|
||||||
}
|
}
|
||||||
var result = $('<div><div><small></small></div><b></b></div>');
|
var result = django.jQuery(
|
||||||
result.find('small').text(state.path.join(' → ')).css({
|
"<div><div><small></small></div><b></b></div>"
|
||||||
'opacity': 0.6,
|
);
|
||||||
'letter-spacing': -0.5
|
result.find("small").text(state.path.join(" → ")).css({
|
||||||
})
|
opacity: 0.6,
|
||||||
result.find('b').text(state.text)
|
"letter-spacing": -0.5,
|
||||||
|
});
|
||||||
|
result.find("b").text(state.text);
|
||||||
return result;
|
return result;
|
||||||
};
|
}
|
||||||
$('.django-select2[name=parent]').djangoSelect2({
|
django.jQuery(".django-select2[name=parent]").djangoSelect2({
|
||||||
templateResult: fmt,
|
templateResult: fmt,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -59,7 +59,7 @@ def apply_smart_search(query, objects):
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
return render(request, "results.html", {"results": Item.get_roots()})
|
return render(request, "results.html", {"results": Item.objects.filter_roots()})
|
||||||
|
|
||||||
|
|
||||||
def search(request):
|
def search(request):
|
||||||
|
|
|
@ -1,31 +1,39 @@
|
||||||
from pkg_resources import parse_version
|
from pkg_resources import parse_version
|
||||||
|
|
||||||
from django_select2.forms import ModelSelect2Widget, HeavySelect2Widget
|
from django_select2.forms import HeavySelect2Widget
|
||||||
from django_hstore.forms import DictionaryFieldWidget
|
|
||||||
|
|
||||||
from django import get_version
|
from django import get_version
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django import forms
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.template import Context
|
from django.template import Context
|
||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
from django.contrib.admin.widgets import AdminTextareaWidget
|
from django.contrib.admin.widgets import AdminTextareaWidget
|
||||||
|
|
||||||
|
from django_admin_hstore_widget.forms import HStoreFormWidget
|
||||||
class ItemSelectWidget(ModelSelect2Widget):
|
from django.contrib.postgres.forms import forms
|
||||||
def __init__(self, *args, **kwargs):
|
from django.templatetags.static import static
|
||||||
kwargs["data_view"] = "item-complete"
|
|
||||||
super(ItemSelectWidget, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def label_from_instance(self, obj):
|
|
||||||
return obj.name
|
|
||||||
|
|
||||||
|
|
||||||
class PropsSelectWidget(DictionaryFieldWidget):
|
class PropsSelectWidget(HStoreFormWidget):
|
||||||
|
@property
|
||||||
|
def media(self):
|
||||||
|
internal_js = [
|
||||||
|
"vendor/jquery/jquery.js",
|
||||||
|
"django_admin_hstore_widget/underscore-min.js",
|
||||||
|
"django_admin_hstore_widget/django_admin_hstore_widget.js",
|
||||||
|
]
|
||||||
|
|
||||||
|
js = [static("admin/js/%s" % path) for path in internal_js]
|
||||||
|
|
||||||
|
return forms.Media(js=js)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def render(self, name, value, attrs=None):
|
def render(self, name, value, attrs=None, renderer=None):
|
||||||
if attrs is None:
|
if attrs is None:
|
||||||
attrs = {}
|
attrs = {}
|
||||||
# it's called "original" because it will be replaced by a copy
|
# it's called "original" because it will be replaced by a copy
|
||||||
|
@ -35,7 +43,7 @@ class PropsSelectWidget(DictionaryFieldWidget):
|
||||||
)
|
)
|
||||||
|
|
||||||
# get default HTML from AdminTextareaWidget
|
# get default HTML from AdminTextareaWidget
|
||||||
html = AdminTextareaWidget.render(self, name, value, attrs)
|
html = AdminTextareaWidget.render(self, name, value, attrs, renderer)
|
||||||
# prepare template context
|
# prepare template context
|
||||||
template_context = {
|
template_context = {
|
||||||
"field_name": name,
|
"field_name": name,
|
||||||
|
@ -46,7 +54,7 @@ class PropsSelectWidget(DictionaryFieldWidget):
|
||||||
"w": w.build_attrs(base_attrs=w.attrs),
|
"w": w.build_attrs(base_attrs=w.attrs),
|
||||||
}
|
}
|
||||||
# get template object
|
# get template object
|
||||||
template = get_template("hstore_%s_widget.html" % self.admin_style)
|
template = get_template("hstore_default_widget.html")
|
||||||
# render additional html
|
# render additional html
|
||||||
additional_html = template.render(template_context)
|
additional_html = template.render(template_context)
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,32 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
<title>{% if title %}{{ title }} - {% endif %}Hackerspace Storage</title>
|
<title>{% if title %}{{ title }} - {% endif %}Hackerspace Storage</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<link rel="stylesheet" href="{% static 'css/bootstrap.css' %}" media="screen">
|
<link
|
||||||
<link rel="stylesheet" href="{% static 'css/custom.css' %}" media="screen">
|
rel="stylesheet"
|
||||||
|
href="{% static 'css/bootstrap.css' %}"
|
||||||
|
media="screen"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="{% static 'css/custom.css' %}"
|
||||||
|
media="screen"
|
||||||
|
/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<nav class="navbar navbar-default">
|
<nav class="navbar navbar-default">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="navbar-header">
|
<div class="navbar-header">
|
||||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
|
<button
|
||||||
|
type="button"
|
||||||
|
class="navbar-toggle collapsed"
|
||||||
|
data-toggle="collapse"
|
||||||
|
data-target="#bs-example-navbar-collapse-1"
|
||||||
|
>
|
||||||
<span class="sr-only">Toggle navigation</span>
|
<span class="sr-only">Toggle navigation</span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
|
@ -26,14 +39,26 @@
|
||||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||||
<ul class="nav navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
<li><a href="/admin/storage/item/add/">Add thing</a></li>
|
<li><a href="/admin/storage/item/add/">Add thing</a></li>
|
||||||
<li><a href="https://wiki.hackerspace.pl/members:services:inventory">How to use</a></li>
|
<li>
|
||||||
|
<a href="https://wiki.hackerspace.pl/members:services:inventory"
|
||||||
|
>How to use</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<form class="navbar-form navbar-right" role="search" action="/search">
|
<form class="navbar-form navbar-right" role="search" action="/search">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" class="form-control" placeholder="Search Hackerspace" name="q" autofocus>
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Search Hackerspace"
|
||||||
|
name="q"
|
||||||
|
autofocus
|
||||||
|
/>
|
||||||
<span class="input-group-btn">
|
<span class="input-group-btn">
|
||||||
<button class="btn btn-primary" type="submit"><i class="glyphicon glyphicon-search"></i></button>
|
<button class="btn btn-primary" type="submit">
|
||||||
|
<i class="glyphicon glyphicon-search"></i>
|
||||||
|
</button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -44,11 +69,9 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<!--<h1 class="page-header">Warsaw Hackerspace <small class="hidden-sm
|
<!--<h1 class="page-header">Warsaw Hackerspace <small class="hidden-sm
|
||||||
hidden-xs">Enjoy your stay</small></h1>-->
|
hidden-xs">Enjoy your stay</small></h1>-->
|
||||||
{% block content %}
|
{% block content %} {% endblock %}
|
||||||
{% endblock %}
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
<script src="{% static 'js/jquery.min.js' %}"></script>
|
|
||||||
<script src="{% static 'js/bootstrap.min.js' %}"></script>
|
<script src="{% static 'js/bootstrap.min.js' %}"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -66,5 +66,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
window.addEventListener("load", function (event) {
|
||||||
django.jQuery(function() { initDjangoHStoreWidget('{{ field_name }}') });
|
django.jQuery(function() { initDjangoHStoreWidget('{{ field_name }}') });
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in New Issue