make the connectors form be able to display all types for connectors

This commit is contained in:
Mikhail Epifanov 2024-01-14 16:59:54 +01:00
parent fb65100b14
commit 245787b89e
No known key found for this signature in database
14 changed files with 302 additions and 93 deletions

View File

@ -16,7 +16,7 @@ from .models import (BookmarkletImport, Comment, CookLog, Food, ImportLog, Ingre
ShoppingList, ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage,
Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog,
TelegramBot, Unit, UnitConversion, UserFile, UserPreference, UserSpace,
ViewLog, HomeAssistantConfig)
ViewLog, HomeAssistantConfig, ExampleConfig)
class CustomUserAdmin(UserAdmin):
@ -103,6 +103,14 @@ class HomeAssistantConfigAdmin(admin.ModelAdmin):
admin.site.register(HomeAssistantConfig, HomeAssistantConfigAdmin)
class ExampleConfigAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'enabled', 'feed_url')
search_fields = ('name',)
admin.site.register(ExampleConfig, ExampleConfigAdmin)
class SyncAdmin(admin.ModelAdmin):
list_display = ('storage', 'path', 'active', 'last_checked')
search_fields = ('storage__name', 'path')

View File

@ -12,14 +12,15 @@ from typing import List, Any, Dict, Optional
from django_scopes import scope
from cookbook.connectors.connector import Connector
from cookbook.connectors.example import Example
from cookbook.connectors.homeassistant import HomeAssistant
from cookbook.models import ShoppingListEntry, Recipe, MealPlan, Space, HomeAssistantConfig, ConnectorConfig
from cookbook.models import ShoppingListEntry, Recipe, MealPlan, Space, HomeAssistantConfig, ExampleConfig
multiprocessing.set_start_method('fork') # https://code.djangoproject.com/ticket/31169
QUEUE_MAX_SIZE = 25
REGISTERED_CLASSES: UnionType = ShoppingListEntry | Recipe | MealPlan
CONNECTOR_UPDATE_CLASSES: UnionType = HomeAssistantConfig | ConnectorConfig
CONNECTOR_UPDATE_CLASSES: UnionType = HomeAssistantConfig | ExampleConfig
class ActionType(Enum):
@ -97,7 +98,10 @@ class ConnectorManager:
loop.run_until_complete(close_connectors(connectors))
with scope(space=space):
connectors: List[Connector] = [HomeAssistant(config) for config in space.homeassistantconfig_set.all() if config.enabled]
connectors: List[Connector] = [
*(HomeAssistant(config) for config in space.homeassistantconfig_set.all() if config.enabled),
*(Example(config) for config in space.exampleconfig_set.all() if config.enabled)
]
_connectors[space.name] = connectors
if len(connectors) == 0 or refresh_connector_cache:

View File

@ -0,0 +1,27 @@
from cookbook.connectors.connector import Connector
from cookbook.models import ExampleConfig, Space, ShoppingListEntry
class Example(Connector):
_config: ExampleConfig
def __init__(self, config: ExampleConfig):
self._config = config
async def on_shopping_list_entry_created(self, space: Space, shopping_list_entry: ShoppingListEntry) -> None:
if not self._config.on_shopping_list_entry_created_enabled:
return
pass
async def on_shopping_list_entry_updated(self, space: Space, shopping_list_entry: ShoppingListEntry) -> None:
if not self._config.on_shopping_list_entry_updated_enabled:
return
pass
async def on_shopping_list_entry_deleted(self, space: Space, shopping_list_entry: ShoppingListEntry) -> None:
if not self._config.on_shopping_list_entry_deleted_enabled:
return
pass
async def close(self) -> None:
pass

View File

@ -10,7 +10,7 @@ from django_scopes.forms import SafeModelChoiceField, SafeModelMultipleChoiceFie
from hcaptcha.fields import hCaptchaField
from .models import (Comment, Food, InviteLink, Keyword, Recipe, RecipeBook, RecipeBookEntry,
SearchPreference, Space, Storage, Sync, User, UserPreference, HomeAssistantConfig)
SearchPreference, Space, Storage, Sync, User, UserPreference, HomeAssistantConfig, ExampleConfig)
class SelectWidget(widgets.Select):
@ -188,12 +188,35 @@ class StorageForm(forms.ModelForm):
}
class HomeAssistantConfigForm(forms.ModelForm):
token = forms.CharField(
widget=forms.TextInput(
attrs={'autocomplete': 'new-password', 'type': 'password'}
),
required=True,
class ConnectorConfigForm(forms.ModelForm):
enabled = forms.BooleanField(
help_text="Is the connector enabled",
required=False,
)
on_shopping_list_entry_created_enabled = forms.BooleanField(
help_text="Enable action for ShoppingListEntry created events",
required=False,
)
on_shopping_list_entry_updated_enabled = forms.BooleanField(
help_text="Enable action for ShoppingListEntry updated events",
required=False,
)
on_shopping_list_entry_deleted_enabled = forms.BooleanField(
help_text="Enable action for ShoppingListEntry deleted events",
required=False,
)
class Meta:
fields = ('name', 'enabled', 'on_shopping_list_entry_created_enabled', 'on_shopping_list_entry_updated_enabled', 'on_shopping_list_entry_deleted_enabled')
class HomeAssistantConfigForm(ConnectorConfigForm):
update_token = forms.CharField(
widget=forms.TextInput(attrs={'autocomplete': 'update-token', 'type': 'password'}),
required=False,
help_text=_('<a href="https://www.home-assistant.io/docs/authentication/#your-account-profile">Long Lived Access Token</a> for your HomeAssistant instance')
)
@ -202,11 +225,6 @@ class HomeAssistantConfigForm(forms.ModelForm):
help_text=_('Something like http://homeassistant.local:8123/api'),
)
enabled = forms.BooleanField(
help_text="Is the HomeAssistantConnector enabled",
required=False,
)
on_shopping_list_entry_created_enabled = forms.BooleanField(
help_text="Enable syncing ShoppingListEntry to Homeassistant Todo List -- Warning: Might have negative performance impact",
required=False,
@ -224,15 +242,24 @@ class HomeAssistantConfigForm(forms.ModelForm):
class Meta:
model = HomeAssistantConfig
fields = (
'name', 'url', 'token', 'todo_entity', 'enabled', 'on_shopping_list_entry_created_enabled', 'on_shopping_list_entry_updated_enabled',
'on_shopping_list_entry_deleted_enabled')
fields = ConnectorConfigForm.Meta.fields + ('url', 'todo_entity')
help_texts = {
'url': _('http://homeassistant.local:8123/api for example'),
}
class ExampleConfigForm(ConnectorConfigForm):
feed_url = forms.URLField(
required=False,
)
class Meta:
model = ExampleConfig
fields = ConnectorConfigForm.Meta.fields + ('feed_url',)
# TODO: Deprecate
class RecipeBookEntryForm(forms.ModelForm):
prefix = 'bookmark'

View File

@ -1,4 +1,4 @@
# Generated by Django 4.2.7 on 2024-01-12 21:01
# Generated by Django 4.2.7 on 2024-01-14 16:00
import cookbook.models
from django.conf import settings
@ -35,4 +35,22 @@ class Migration(migrations.Migration):
},
bases=(models.Model, cookbook.models.PermissionModelMixin),
),
migrations.CreateModel(
name='ExampleConfig',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=128, validators=[django.core.validators.MinLengthValidator(1)])),
('enabled', models.BooleanField(default=True, help_text='Is Connector Enabled')),
('on_shopping_list_entry_created_enabled', models.BooleanField(default=False)),
('on_shopping_list_entry_updated_enabled', models.BooleanField(default=False)),
('on_shopping_list_entry_deleted_enabled', models.BooleanField(default=False)),
('feed_url', models.URLField(blank=True)),
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
('space', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space')),
],
options={
'abstract': False,
},
bases=(models.Model, cookbook.models.PermissionModelMixin),
),
]

View File

@ -340,6 +340,7 @@ class Space(ExportModelOperationsMixin('space'), models.Model):
Sync.objects.filter(space=self).delete()
Storage.objects.filter(space=self).delete()
HomeAssistantConfig.objects.filter(space=self).delete()
ExampleConfig.objects.filter(space=self).delete()
ShoppingListEntry.objects.filter(shoppinglist__space=self).delete()
ShoppingListRecipe.objects.filter(shoppinglist__space=self).delete()
@ -387,6 +388,10 @@ class HomeAssistantConfig(ConnectorConfig):
todo_entity = models.CharField(max_length=128, default='todo.shopping_list')
class ExampleConfig(ConnectorConfig):
feed_url = models.URLField(blank=True)
class UserPreference(models.Model, PermissionModelMixin):
# Themes
BOOTSTRAP = 'BOOTSTRAP'

View File

@ -1,9 +1,14 @@
from typing import Any, Dict
import django_tables2 as tables
from django.utils.html import format_html
from django.utils.translation import gettext as _
from django.views.generic import TemplateView
from django_tables2 import MultiTableMixin
from django_tables2.utils import A
from .models import CookLog, InviteLink, RecipeImport, Storage, Sync, SyncLog, ViewLog, HomeAssistantConfig
from .helper.permission_helper import GroupRequiredMixin
from .models import CookLog, InviteLink, RecipeImport, Storage, Sync, SyncLog, ViewLog, HomeAssistantConfig, ExampleConfig
class StorageTable(tables.Table):
@ -15,6 +20,16 @@ class StorageTable(tables.Table):
fields = ('id', 'name', 'method')
class ExampleConfigTable(tables.Table):
id = tables.LinkColumn('edit_example_config', args=[A('id')])
class Meta:
model = ExampleConfig
template_name = 'generic/table_template.html'
fields = ('id', 'name', 'enabled', 'feed_url')
attrs = {'table_name': "Example Configs", 'create_url': 'new_example_config'}
class HomeAssistantConfigTable(tables.Table):
id = tables.LinkColumn('edit_home_assistant_config', args=[A('id')])
@ -22,6 +37,30 @@ class HomeAssistantConfigTable(tables.Table):
model = HomeAssistantConfig
template_name = 'generic/table_template.html'
fields = ('id', 'name', 'enabled', 'url')
attrs = {'table_name': "HomeAssistant Configs", 'create_url': 'new_home_assistant_config'}
class ConnectorConfigTable(GroupRequiredMixin, MultiTableMixin, TemplateView):
groups_required = ['admin']
template_name = "list_connectors.html"
table_pagination = {
"per_page": 25
}
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
kwargs = super().get_context_data(**kwargs)
kwargs['title'] = _("Connectors")
return kwargs
def get_tables(self):
example_configs = ExampleConfig.objects.filter(space=self.request.space).all()
home_assistant_configs = HomeAssistantConfig.objects.filter(space=self.request.space).all()
return [
ExampleConfigTable(example_configs),
HomeAssistantConfigTable(home_assistant_configs)
]
class ImportLogTable(tables.Table):

View File

@ -336,7 +336,7 @@
class="fas fa-server fa-fw"></i> {% trans 'Space Settings' %}</a>
{% endif %}
{% if request.user == request.space.created_by or user.is_superuser %}
<a class="dropdown-item" href="{% url 'list_home_assistant_config' %}"><i
<a class="dropdown-item" href="{% url 'list_connectors' %}"><i
class="fas fa-sync-alt fa-fw"></i> {% trans 'External Connectors' %}</a>
{% endif %}
{% if user.is_superuser %}

View File

@ -0,0 +1,55 @@
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% load i18n %}
{% load django_tables2 %}
{% block title %}{% trans 'List' %}{% endblock %}
{% block content %}
{% if request.resolver_match.url_name in 'list_storage,list_recipe_import,list_sync_log' %}
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'data_sync' %}">{% trans 'External Recipes' %}</a></li>
<li class="breadcrumb-item active" aria-current="page">{{ title }}</li>
</ol>
</nav>
{% endif %}
<div class="table-container">
<span class="col col-md-9">
<h3 style="margin-bottom: 2vh">{{ title }} {% trans 'List' %}</h3>
</span>
{% if filter %}
<br/>
<br/>
<form action="." method="get">
{% csrf_token %}
{{ filter.form|crispy }}
<button type="submit" class="btn btn-success">{% trans 'Filter' %}</button>
</form>
{% endif %}
{% if import_btn %}
<a href="{% url 'data_batch_import' %}" class="btn btn-warning">{% trans 'Import all' %}</a>
<br/>
<br/>
{% endif %}
{% for table in tables %}
<span class="col col-md-9">
<h3 style="margin-bottom: 2vh">{{ table.attrs.table_name }} {% trans 'List' %}
{% if table.attrs.create_url %}
<a href="{% url table.attrs.create_url %}"> <i class="fas fa-plus-circle"></i> </a>
{% endif %}
</h3>
</span>
{% render_table table %}
{% endfor %}
</div>
{% endblock content %}

View File

@ -12,7 +12,8 @@ from recipes.settings import DEBUG, PLUGINS
from .models import (Automation, Comment, CustomFilter, Food, InviteLink, Keyword, PropertyType,
Recipe, RecipeBook, RecipeBookEntry, RecipeImport, ShoppingList, Space, Step,
Storage, Supermarket, SupermarketCategory, Sync, SyncLog, Unit, UnitConversion,
UserFile, UserSpace, get_model_name, HomeAssistantConfig)
UserFile, UserSpace, get_model_name, HomeAssistantConfig, ExampleConfig)
from .tables import ConnectorConfigTable
from .views import api, data, delete, edit, import_export, lists, new, telegram, views
from .views.api import CustomAuthToken, ImportOpenData
@ -115,7 +116,7 @@ urlpatterns = [
path('edit/recipe/convert/<int:pk>/', edit.convert_recipe, name='edit_convert_recipe'),
path('edit/storage/<int:pk>/', edit.edit_storage, name='edit_storage'),
path('edit/home-assistant-config/<int:pk>/', edit.edit_home_assistant_config, name='edit_home_assistant_config'),
path('list/connectors', ConnectorConfigTable.as_view(), name='list_connectors'),
path('delete/recipe-source/<int:pk>/', delete.delete_recipe_source, name='delete_recipe_source'),
@ -168,7 +169,7 @@ urlpatterns = [
]
generic_models = (
Recipe, RecipeImport, Storage, HomeAssistantConfig, RecipeBook, SyncLog, Sync,
Recipe, RecipeImport, Storage, HomeAssistantConfig, ExampleConfig, RecipeBook, SyncLog, Sync,
Comment, RecipeBookEntry, ShoppingList, InviteLink, UserSpace, Space
)

View File

@ -9,7 +9,7 @@ from django.views.generic import DeleteView
from cookbook.helper.permission_helper import GroupRequiredMixin, OwnerRequiredMixin, group_required
from cookbook.models import (Comment, InviteLink, MealPlan, Recipe, RecipeBook, RecipeBookEntry,
RecipeImport, Space, Storage, Sync, UserSpace, HomeAssistantConfig)
RecipeImport, Space, Storage, Sync, UserSpace, HomeAssistantConfig, ExampleConfig)
from cookbook.provider.dropbox import Dropbox
from cookbook.provider.local import Local
from cookbook.provider.nextcloud import Nextcloud
@ -126,23 +126,24 @@ class HomeAssistantConfigDelete(GroupRequiredMixin, DeleteView):
groups_required = ['admin']
template_name = "generic/delete_template.html"
model = HomeAssistantConfig
success_url = reverse_lazy('list_storage')
success_url = reverse_lazy('list_connectors')
def get_context_data(self, **kwargs):
context = super(HomeAssistantConfigDelete, self).get_context_data(**kwargs)
context = super().get_context_data(**kwargs)
context['title'] = _("HomeAssistant Config Backend")
return context
def post(self, request, *args, **kwargs):
try:
return self.delete(request, *args, **kwargs)
except ProtectedError:
messages.add_message(
request,
messages.WARNING,
_('Could not delete this storage backend as it is used in at least one monitor.') # noqa: E501
)
return HttpResponseRedirect(reverse('list_storage'))
class ExampleConfigDelete(GroupRequiredMixin, DeleteView):
groups_required = ['admin']
template_name = "generic/delete_template.html"
model = ExampleConfig
success_url = reverse_lazy('list_connectors')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = _("Example Config Backend")
return context
class CommentDelete(OwnerRequiredMixin, DeleteView):

View File

@ -9,10 +9,10 @@ from django.utils.translation import gettext as _
from django.views.generic import UpdateView
from django.views.generic.edit import FormMixin
from cookbook.forms import CommentForm, ExternalRecipeForm, StorageForm, SyncForm, HomeAssistantConfigForm
from cookbook.forms import CommentForm, ExternalRecipeForm, StorageForm, SyncForm, HomeAssistantConfigForm, ExampleConfigForm
from cookbook.helper.permission_helper import (GroupRequiredMixin, OwnerRequiredMixin,
above_space_limit, group_required)
from cookbook.models import Comment, Recipe, RecipeImport, Storage, Sync, HomeAssistantConfig
from cookbook.models import Comment, Recipe, RecipeImport, Storage, Sync, HomeAssistantConfig, ExampleConfig
from cookbook.provider.dropbox import Dropbox
from cookbook.provider.local import Local
from cookbook.provider.nextcloud import Nextcloud
@ -128,46 +128,49 @@ def edit_storage(request, pk):
)
@group_required('admin')
def edit_home_assistant_config(request, pk):
instance: HomeAssistantConfig = get_object_or_404(HomeAssistantConfig, pk=pk, space=request.space)
class HomeAssistantConfigUpdate(GroupRequiredMixin, UpdateView):
groups_required = ['admin']
template_name = "generic/edit_template.html"
model = HomeAssistantConfig
form_class = HomeAssistantConfigForm
if not (instance.created_by == request.user or request.user.is_superuser):
messages.add_message(request, messages.ERROR, _('You cannot edit this homeassistant config!'))
return HttpResponseRedirect(reverse('edit_home_assistant_config'))
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['initial']['update_token'] = VALUE_NOT_CHANGED
return kwargs
if request.space.demo or settings.HOSTED:
messages.add_message(request, messages.ERROR, _('This feature is not yet available in the hosted version of tandoor!'))
return redirect('index')
def form_valid(self, form):
if form.cleaned_data['update_token'] != VALUE_NOT_CHANGED and form.cleaned_data['update_token'] != "":
form.instance.token = form.cleaned_data['update_token']
messages.add_message(self.request, messages.SUCCESS, _('Config saved!'))
return super(HomeAssistantConfigUpdate, self).form_valid(form)
if request.method == "POST":
form = HomeAssistantConfigForm(request.POST, instance=copy.deepcopy(instance))
if form.is_valid():
instance.name = form.cleaned_data['name']
instance.url = form.cleaned_data['url']
instance.todo_entity = form.cleaned_data['todo_entity']
instance.enabled = form.cleaned_data['enabled']
instance.on_shopping_list_entry_created_enabled = form.cleaned_data['on_shopping_list_entry_created_enabled']
instance.on_shopping_list_entry_updated_enabled = form.cleaned_data['on_shopping_list_entry_updated_enabled']
instance.on_shopping_list_entry_deleted_enabled = form.cleaned_data['on_shopping_list_entry_deleted_enabled']
def get_success_url(self):
return reverse('edit_home_assistant_config', kwargs={'pk': self.object.pk})
if form.cleaned_data['token'] != VALUE_NOT_CHANGED:
instance.token = form.cleaned_data['token']
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = _("HomeAssistantConfig")
return context
instance.save()
messages.add_message(request, messages.SUCCESS, _('HomeAssistant config saved!'))
else:
messages.add_message(request, messages.ERROR, _('There was an error updating this config!'))
else:
instance.token = VALUE_NOT_CHANGED
form = HomeAssistantConfigForm(instance=instance)
class ExampleConfigUpdate(GroupRequiredMixin, UpdateView):
groups_required = ['admin']
template_name = "generic/edit_template.html"
model = ExampleConfig
form_class = ExampleConfigForm
return render(
request,
'generic/edit_template.html',
{'form': form, 'title': _('HomeAssistantConfig')}
)
def form_valid(self, form):
messages.add_message(self.request, messages.SUCCESS, _('Config saved!'))
return super().form_valid(form)
def get_success_url(self):
return reverse('edit_example_config', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = _("ExampleConfig")
return context
class CommentUpdate(OwnerRequiredMixin, UpdateView):

View File

@ -6,8 +6,8 @@ from django.utils.translation import gettext as _
from django_tables2 import RequestConfig
from cookbook.helper.permission_helper import group_required
from cookbook.models import InviteLink, RecipeImport, Storage, SyncLog, UserFile, HomeAssistantConfig
from cookbook.tables import ImportLogTable, InviteLinkTable, RecipeImportTable, StorageTable, HomeAssistantConfigTable
from cookbook.models import InviteLink, RecipeImport, Storage, SyncLog, UserFile
from cookbook.tables import ImportLogTable, InviteLinkTable, RecipeImportTable, StorageTable
@group_required('admin')
@ -65,19 +65,6 @@ def storage(request):
)
@group_required('admin')
def home_assistant_config(request):
table = HomeAssistantConfigTable(HomeAssistantConfig.objects.filter(space=request.space).all())
RequestConfig(request, paginate={'per_page': 25}).configure(table)
return render(
request, 'generic/list_template.html', {
'title': _("HomeAssistant Config Backend"),
'table': table,
'create_url': 'new_home_assistant_config'
})
@group_required('admin')
def invite_link(request):
table = InviteLinkTable(

View File

@ -5,9 +5,9 @@ from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext as _
from django.views.generic import CreateView
from cookbook.forms import ImportRecipeForm, Storage, StorageForm, HomeAssistantConfigForm
from cookbook.forms import ImportRecipeForm, Storage, StorageForm, HomeAssistantConfigForm, ExampleConfigForm
from cookbook.helper.permission_helper import GroupRequiredMixin, above_space_limit, group_required
from cookbook.models import Recipe, RecipeImport, ShareLink, Step, HomeAssistantConfig
from cookbook.models import Recipe, RecipeImport, ShareLink, Step, HomeAssistantConfig, ExampleConfig
from recipes import settings
@ -77,6 +77,40 @@ class HomeAssistantConfigCreate(GroupRequiredMixin, CreateView):
form_class = HomeAssistantConfigForm
success_url = reverse_lazy('list_home_assistant_config')
def get_form_class(self):
form_class = super().get_form_class()
if self.request.method == 'GET':
update_token_field = form_class.base_fields['update_token']
update_token_field.required = True
return form_class
def form_valid(self, form):
if self.request.space.demo or settings.HOSTED:
messages.add_message(self.request, messages.ERROR, _('This feature is not yet available in the hosted version of tandoor!'))
return redirect('index')
obj = form.save(commit=False)
obj.token = form.cleaned_data['update_token']
obj.created_by = self.request.user
obj.space = self.request.space
obj.save()
return HttpResponseRedirect(reverse('edit_home_assistant_config', kwargs={'pk': obj.pk}))
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = _("HomeAssistant Config Backend")
return context
class ExampleConfigCreate(GroupRequiredMixin, CreateView):
groups_required = ['admin']
template_name = "generic/new_template.html"
model = ExampleConfig
form_class = ExampleConfigForm
success_url = reverse_lazy('list_connectors')
def form_valid(self, form):
if self.request.space.demo or settings.HOSTED:
messages.add_message(self.request, messages.ERROR, _('This feature is not yet available in the hosted version of tandoor!'))
@ -86,11 +120,11 @@ class HomeAssistantConfigCreate(GroupRequiredMixin, CreateView):
obj.created_by = self.request.user
obj.space = self.request.space
obj.save()
return HttpResponseRedirect(reverse('edit_home_assistant_config', kwargs={'pk': obj.pk}))
return HttpResponseRedirect(reverse('edit_example_config', kwargs={'pk': obj.pk}))
def get_context_data(self, **kwargs):
context = super(HomeAssistantConfigCreate, self).get_context_data(**kwargs)
context['title'] = _("HomeAssistant Config Backend")
context = super().get_context_data(**kwargs)
context['title'] = _("Example Config Backend")
return context