diff --git a/data/db.sqlite3 b/data/db.sqlite3 index 60f3aa4..6d07b08 100644 Binary files a/data/db.sqlite3 and b/data/db.sqlite3 differ diff --git a/links/forms.py b/links/forms.py index eb71ceb..80b42f1 100644 --- a/links/forms.py +++ b/links/forms.py @@ -2,6 +2,8 @@ from django import forms from .models import Link from simplemde.fields import SimpleMDEField from django.utils.translation import gettext_lazy as _ +from django.core.validators import URLValidator +import re class LinkForm(forms.ModelForm): text = SimpleMDEField() @@ -14,6 +16,28 @@ class LinkForm(forms.ModelForm): 'description': forms.Textarea(attrs={'rows': 3}), } + def clean_original_url(self): + url = self.cleaned_data.get('original_url') + if not url: + return url + + # Extract template parameters + template_params = re.findall(r'\{([^{}]*)\}', url) + + # Temporarily replace template parameters with placeholder + temp_url = url + for param in template_params: + param_full = '{' + param + '}' + temp_url = temp_url.replace(param_full, 'template-param') + + # Validate the URL with placeholders + try: + URLValidator()(temp_url) + except forms.ValidationError: + raise forms.ValidationError(_("Please enter a valid URL. Template parameters are allowed in the format {param_name,default=value}.")) + + return url + def clean(self): cleaned_data = super().clean() link_type = cleaned_data.get('link_type') diff --git a/links/migrations/0008_alter_link_original_url.py b/links/migrations/0008_alter_link_original_url.py new file mode 100644 index 0000000..3941461 --- /dev/null +++ b/links/migrations/0008_alter_link_original_url.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.8 on 2024-11-02 11:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('links', '0007_link_description'), + ] + + operations = [ + migrations.AlterField( + model_name='link', + name='original_url', + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/links/models.py b/links/models.py index 54d9f20..a1fd61b 100644 --- a/links/models.py +++ b/links/models.py @@ -2,6 +2,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from django.urls import reverse import logging +import re logger = logging.getLogger(__name__) @@ -11,8 +12,8 @@ class Link(models.Model): CUSTOM = 'CUSTOM', _('Custom') alias = models.SlugField(max_length=100, unique=True) - original_url = models.URLField(blank=True, null=True) - text = models.TextField(blank=True, null=True) # 改为 TextField + original_url = models.TextField(blank=True, null=True) + text = models.TextField(blank=True, null=True) link_type = models.CharField( max_length=10, choices=LinkType.choices, @@ -23,6 +24,71 @@ class Link(models.Model): updated_at = models.DateTimeField(auto_now=True) description = models.TextField(blank=True, null=True, verbose_name=_("Description")) + def get_template_parameters(self): + """Extract template parameters and their default values from original_url""" + pattern = r'\{([^{}]*)\}' + matches = re.finditer(pattern, self.original_url) + params = {} + + for match in matches: + param_str = match.group(1) + if ',' in param_str: + param_name, default_value = param_str.split(',', 1) + default_value = default_value.strip() + if default_value.startswith('default='): + default_value = default_value[8:].strip('"\'') + params[param_name.strip()] = default_value + else: + params[param_str.strip()] = None + + return params + + def get_processed_url(self, **kwargs): + """Process template URL with provided parameters""" + if not self.original_url: + return None + + processed_url = self.original_url + pattern = r'\{([^{}]*)\}' + matches = re.finditer(pattern, self.original_url) + + # 收集所有参数及其默认值 + params = {} + for match in matches: + param_str = match.group(1) + param_full = '{' + param_str + '}' + + # 处理参数字符串 + param_parts = [p.strip() for p in param_str.split(',', 1)] + param_name = param_parts[0].strip() + default_value = None + + if len(param_parts) > 1: + # 处理默认值部分 + default_part = param_parts[1].strip() + if default_part.startswith('default='): + default_value = default_part[8:].strip() # 移除 'default=' 前缀 + # 移除引号(如果存在) + if (default_value.startswith('"') and default_value.endswith('"')) or \ + (default_value.startswith("'") and default_value.endswith("'")): + default_value = default_value[1:-1] + + params[param_name] = { + 'default': default_value, + 'full_match': param_full + } + + # 替换所有参数 + for param_name, param_info in params.items(): + value = kwargs.get(param_name, param_info['default']) + if value is None: + raise ValueError(f"No value provided for parameter {param_name}") + + # 使用完整的匹配模式进行替换 + processed_url = processed_url.replace(param_info['full_match'], str(value)) + + return processed_url + def __str__(self): return self.alias diff --git a/links/patches.py b/links/patches.py new file mode 100644 index 0000000..e137f1c --- /dev/null +++ b/links/patches.py @@ -0,0 +1,4 @@ +import sys +from django import urls + +sys.modules['django.core.urlresolvers'] = urls diff --git a/links/templates/links/help.html b/links/templates/links/help.html new file mode 100644 index 0000000..d4ac24b --- /dev/null +++ b/links/templates/links/help.html @@ -0,0 +1,141 @@ +{% extends 'base.html' %} +{% load i18n %} +{% load markdown_extras %} + +{% block extra_css %} + + + +{% endblock %} + +{% block content %} +
+ {% trans "Template type allows you to create dynamic URLs with parameters. Use {query, default=value} syntax in the URL." %} +
++ {% trans "Example: https://google.com/search?q={query, default=hello}" %} +
+